Skip to content

Commit

Permalink
feature: pass selectors instead of element references
Browse files Browse the repository at this point in the history
closes #485
  • Loading branch information
g105b committed Apr 26, 2024
1 parent 0cc70a4 commit 0649566
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 13 deletions.
12 changes: 6 additions & 6 deletions src/Binder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
abstract class Binder {
abstract public function bindValue(
mixed $value,
?Element $context = null
null|string|Element $context = null
):void;

/**
Expand All @@ -16,7 +16,7 @@ abstract public function bindValue(
abstract public function bindKeyValue(
string $key,
mixed $value,
?Element $context = null
null|string|Element $context = null
):void;

/**
Expand All @@ -25,12 +25,12 @@ abstract public function bindKeyValue(
*/
abstract public function bindData(
mixed $kvp,
?Element $context = null
null|string|Element $context = null
):void;

abstract public function bindTable(
mixed $tableData,
?Element $context = null,
null|string|Element $context = null,
?string $bindKey = null
):void;

Expand All @@ -39,15 +39,15 @@ abstract public function bindTable(
*/
abstract public function bindList(
iterable $listData,
?Element $context = null,
null|string|Element $context = null,
?string $templateName = null
):int;

/** @param iterable<int, mixed> $listData */
abstract public function bindListCallback(
iterable $listData,
callable $callback,
?Element $context = null,
null|string|Element $context = null,
?string $templateName = null
):int;
}
10 changes: 9 additions & 1 deletion src/ComponentBinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ public function setComponentBinderDependencies(Element $componentElement):void {

public function bindList(
iterable $listData,
?Element $context = null,
null|string|Element $context = null,
?string $templateName = null
):int {
if(is_string($context)) {
$context = $this->stringToContext($context);
}

if($context) {
$this->checkElementContainedWithinComponent($context);
}
Expand Down Expand Up @@ -53,4 +57,8 @@ private function checkElementContainedWithinComponent(Element $context):void {
);
}
}

protected function stringToContext(string $context):Element {
return $this->componentElement->querySelector($context);
}
}
36 changes: 30 additions & 6 deletions src/DocumentBinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ public function setDependencies(
*/
public function bindValue(
mixed $value,
?Element $context = null
null|string|Element $context = null
):void {
if(is_string($context)) {
$context = $this->stringToContext($context);
}

$this->bind(null, $value, $context);
}

Expand All @@ -51,8 +55,12 @@ public function bindValue(
public function bindKeyValue(
string $key,
mixed $value,
?Element $context = null
null|Element|string $context = null,
):void {
if(is_string($context)) {
$context = $this->stringToContext($context);
}

$this->bind($key, $value, $context);
}

Expand All @@ -62,8 +70,12 @@ public function bindKeyValue(
*/
public function bindData(
mixed $kvp,
?Element $context = null
null|string|Element $context = null
):void {
if(is_string($context)) {
$context = $this->stringToContext($context);
}

if($this->isIndexedArray($kvp)) {
throw new IncompatibleBindDataException("bindData is only compatible with key-value-pair data, but it was passed an indexed array.");
}
Expand Down Expand Up @@ -94,9 +106,13 @@ public function bindData(

public function bindTable(
mixed $tableData,
?Element $context = null,
null|string|Element $context = null,
?string $bindKey = null
):void {
if(is_string($context)) {
$context = $this->stringToContext($context);

Check warning on line 113 in src/DocumentBinder.php

View check run for this annotation

Codecov / codecov/patch

src/DocumentBinder.php#L113

Added line #L113 was not covered by tests
}

$this->tableBinder->bindTableData(
$tableData,
$context ?? $this->document,
Expand All @@ -109,9 +125,13 @@ public function bindTable(
*/
public function bindList(
iterable $listData,
?Element $context = null,
null|string|Element $context = null,
?string $templateName = null
):int {
if(is_string($context)) {
$context = $this->stringToContext($context);

Check warning on line 132 in src/DocumentBinder.php

View check run for this annotation

Codecov / codecov/patch

src/DocumentBinder.php#L132

Added line #L132 was not covered by tests
}

if(!$context) {
$context = $this->document;
}
Expand All @@ -123,7 +143,7 @@ public function bindList(
public function bindListCallback(
iterable $listData,
callable $callback,
?Element $context = null,
null|string|Element $context = null,
?string $templateName = null
):int {
if(!$context) {
Expand Down Expand Up @@ -192,4 +212,8 @@ private function isIndexedArray(mixed $data):bool {

return true;
}

protected function stringToContext(string $context):Element {
return $this->document->querySelector($context);

Check warning on line 217 in src/DocumentBinder.php

View check run for this annotation

Codecov / codecov/patch

src/DocumentBinder.php#L216-L217

Added lines #L216 - L217 were not covered by tests
}
}
142 changes: 142 additions & 0 deletions test/phpunit/ComponentBinderTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php
namespace Gt\DomTemplate\Test;
use Gt\Dom\Element;
use Gt\Dom\HTMLDocument;
use Gt\DomTemplate\BindableCache;
use Gt\DomTemplate\ComponentBinder;
Expand Down Expand Up @@ -71,4 +72,145 @@ public function testBindKeyValue():void {
$sut->setComponentBinderDependencies($componentElement);
$sut->bindKeyValue("listTitle", "This should change!");
}

public function testBindKeyValue_stringContext():void {
$document = new HTMLDocument(HTMLPageContent::HTML_COMPONENT_WITH_ATTRIBUTE_NESTED);
$componentElement = $document->querySelector("example-component");
$subComponent1 = $document->querySelector("#subcomponent-1");
$subComponent2 = $document->querySelector("#subcomponent-2");

$elementBinder = self::createMock(ElementBinder::class);
$bindMatcher = self::exactly(3);
$elementBinder->expects($bindMatcher)
->method("bind")
->willReturnCallback(function(string $key, string $value, Element $element)use($bindMatcher, $componentElement, $subComponent1, $subComponent2):void {
match($bindMatcher->numberOfInvocations()) {
1 => self::assertEquals(["title", "Title 1!", $subComponent1], [$key, $value, $element]),
2 => self::assertEquals(["title", "Title 2!", $subComponent2], [$key, $value, $element]),
3 => self::assertEquals(["title", "Main title!", $componentElement], [$key, $value, $element]),
};
});

$sut = new ComponentBinder($document);
$sut->setDependencies(
$elementBinder,
self::createMock(PlaceholderBinder::class),
self::createMock(TableBinder::class),
self::createMock(ListBinder::class),
self::createMock(ListElementCollection::class),
self::createMock(BindableCache::class),
);
$sut->setComponentBinderDependencies($componentElement);

$sut->bindKeyValue("title", "Title 1!", "#subcomponent-1");
$sut->bindKeyValue("title", "Title 2!", "#subcomponent-2");
$sut->bindKeyValue("title", "Main title!");
}

public function testBindData_stringContext():void {
$document = new HTMLDocument(HTMLPageContent::HTML_COMPONENT_WITH_ATTRIBUTE_NESTED);
$componentElement = $document->querySelector("example-component");
$subComponent1 = $document->querySelector("#subcomponent-1");
$subComponent2 = $document->querySelector("#subcomponent-2");

$elementBinder = self::createMock(ElementBinder::class);
$bindMatcher = self::exactly(6);
$elementBinder->expects($bindMatcher)
->method("bind")
->willReturnCallback(function(string $key, string $value, Element $element)use($bindMatcher, $componentElement, $subComponent1, $subComponent2):void {
match($bindMatcher->numberOfInvocations()) {
1 => self::assertEquals(["title", "Title 1!", $subComponent1], [$key, $value, $element]),
2 => self::assertEquals(["number", "1", $subComponent1], [$key, $value, $element]),
3 => self::assertEquals(["title", "Title 2!", $subComponent2], [$key, $value, $element]),
4 => self::assertEquals(["number", "2", $subComponent2], [$key, $value, $element]),
5 => self::assertEquals(["title", "Main title!", $componentElement], [$key, $value, $element]),
6 => self::assertEquals(["number", "3", $componentElement], [$key, $value, $element]),
};
});

$sut = new ComponentBinder($document);
$sut->setDependencies(
$elementBinder,
self::createMock(PlaceholderBinder::class),
self::createMock(TableBinder::class),
self::createMock(ListBinder::class),
self::createMock(ListElementCollection::class),
self::createMock(BindableCache::class),
);
$sut->setComponentBinderDependencies($componentElement);

$sut->bindData(["title" => "Title 1!", "number" => "1"], "#subcomponent-1");
$sut->bindData(["title" => "Title 2!", "number" => "2"], "#subcomponent-2");
$sut->bindData(["title" => "Main title!", "number" => "3"]);
}

public function testBindList_stringContext():void {
$document = new HTMLDocument(HTMLPageContent::HTML_COMPONENT_WITH_ATTRIBUTE_NESTED);
$componentElement = $document->querySelector("example-component");
$subComponent1 = $document->querySelector("#subcomponent-1");
$subComponent2 = $document->querySelector("#subcomponent-2");

$listBinder = self::createMock(ListBinder::class);
$bindMatcher = self::exactly(3);
$listBinder->expects($bindMatcher)
->method("bindListData")
->willReturnCallback(function(array $listData, Element $context)use($bindMatcher, $componentElement, $subComponent1, $subComponent2):int {
match($bindMatcher->numberOfInvocations()) {
1 => self::assertEquals([["List", "for", "component 2"], $subComponent2], [$listData, $context]),
2 => self::assertEquals([["List", "for", "component 1"], $subComponent1], [$listData, $context]),
3 => self::assertEquals([["List", "for", "main component"], $componentElement], [$listData, $context]),
};

return 0;
});

$sut = new ComponentBinder($document);
$sut->setDependencies(
self::createMock(ElementBinder::class),
self::createMock(PlaceholderBinder::class),
self::createMock(TableBinder::class),
$listBinder,
self::createMock(ListElementCollection::class),
self::createMock(BindableCache::class),
);
$sut->setComponentBinderDependencies($componentElement);

$sut->bindList(["List", "for", "component 2"], "#subcomponent-2");
$sut->bindList(["List", "for", "component 1"], "#subcomponent-1");
$sut->bindList(["List", "for", "main component"]);
}

public function testBindValue_stringContext():void {
$document = new HTMLDocument(HTMLPageContent::HTML_COMPONENT_WITH_ATTRIBUTE_NESTED);
$componentElement = $document->querySelector("example-component");
$subComponent1 = $document->querySelector("#subcomponent-1");
$subComponent2 = $document->querySelector("#subcomponent-2");

$elementBinder = self::createMock(ElementBinder::class);
$bindMatcher = self::exactly(3);
$elementBinder->expects($bindMatcher)
->method("bind")
->willReturnCallback(function(?string $key, string $value, Element $element)use($bindMatcher, $componentElement, $subComponent1, $subComponent2):void {
match($bindMatcher->numberOfInvocations()) {
1 => self::assertEquals([null, "1", $subComponent1], [$key, $value, $element]),
2 => self::assertEquals([null, "2", $subComponent2], [$key, $value, $element]),
3 => self::assertEquals([null, "3", $componentElement], [$key, $value, $element]),
};
});

$sut = new ComponentBinder($document);
$sut->setDependencies(
$elementBinder,
self::createMock(PlaceholderBinder::class),
self::createMock(TableBinder::class),
self::createMock(ListBinder::class),
self::createMock(ListElementCollection::class),
self::createMock(BindableCache::class),
);
$sut->setComponentBinderDependencies($componentElement);

$sut->bindValue("1", "#subcomponent-1");
$sut->bindValue("2", "#subcomponent-2");
$sut->bindValue("3");
}
}
16 changes: 16 additions & 0 deletions test/phpunit/TestHelper/HTMLPageContent.php
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,22 @@ class HTMLPageContent {
</html>
HTML;

const HTML_COMPONENT_WITH_ATTRIBUTE_NESTED = <<<HTML
<!doctype html>
<h1>Hello, World!</h1>
<example-component>
<h2 data-bind:text="title">Component title</h2>
<example-subcomponent id="subcomponent-1">
<h3 data-bind:text="title">Sub-component title</h3>
</example-subcomponent>
<example-subcomponent id="subcomponent-2">
<h3 data-bind:text="title">Sub-component title</h3>
</example-subcomponent>
</example-component>
HTML;


const HTML_COMPONENT_NESTED_OUTER = <<<HTML
<!doctype html>
Expand Down

0 comments on commit 0649566

Please sign in to comment.