Skip to content

Commit

Permalink
Rename WebView::setJsVar() to WebView::setJsVars() and change syntax …
Browse files Browse the repository at this point in the history
…for set JS vars and strings (#145)
  • Loading branch information
vjik committed May 9, 2021
1 parent c1a467d commit acff1ba
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 13 deletions.
133 changes: 120 additions & 13 deletions src/WebView.php
Expand Up @@ -4,13 +4,22 @@

namespace Yiisoft\View;

use InvalidArgumentException;
use Yiisoft\Arrays\ArrayHelper;
use Yiisoft\Html\Html;
use Yiisoft\Html\Tag\Script;
use Yiisoft\View\Event\BodyBegin;
use Yiisoft\View\Event\BodyEnd;
use Yiisoft\View\Event\PageEnd;

use function array_key_exists;
use function get_class;
use function gettype;
use function in_array;
use function is_array;
use function is_object;
use function is_string;

/**
* View represents a view object in the MVC pattern.
*
Expand Down Expand Up @@ -63,6 +72,9 @@ class WebView extends View
*/
public const POSITION_LOAD = 5;

private const DEFAULT_POSITION_JS_VARIABLE = self::POSITION_HEAD;
private const DEFAULT_POSITION_JS_STRING = self::POSITION_END;

/**
* This is internally used as the placeholder for receiving the content registered for the head section.
*/
Expand Down Expand Up @@ -328,7 +340,7 @@ public function registerCssFile(string $url, array $options = [], string $key =
* @param string $key the key that identifies the JS code block. If null, it will use $js as the key. If two JS code
* blocks are registered with the same key, the latter will overwrite the former.
*/
public function registerJs(string $js, int $position = self::POSITION_END, string $key = null): void
public function registerJs(string $js, int $position = self::POSITION_END, ?string $key = null): void
{
$key = $key ?: md5($js);
$this->js[$position][$key] = $js;
Expand Down Expand Up @@ -557,30 +569,35 @@ public function setJsFiles(array $jsFiles): void
* It processes the JS strings generated by the asset manager.
*
* @param array $jsStrings
*
* @throws InvalidArgumentException
*/
public function setJsStrings(array $jsStrings): void
{
foreach ($jsStrings as $value) {
$this->registerJs(
$value['string'],
$value['attributes']['position']
/** @var mixed $value */
foreach ($jsStrings as $key => $value) {
$this->registerJsStringByConfig(
is_string($key) ? $key : null,
is_array($value) ? $value : [$value, self::DEFAULT_POSITION_JS_STRING]
);
}
}

/**
* It processes the JS variables generated by the asset manager and converts it into JS code.
*
* @param array $jsVar
* @param array $jsVars
*
* @throws InvalidArgumentException
*/
public function setJsVar(array $jsVar): void
public function setJsVars(array $jsVars): void
{
foreach ($jsVar as $key => $value) {
$this->registerJsVar(
(string)$key,
$value['variables'],
$value['attributes']['position']
);
foreach ($jsVars as $key => $value) {
if (is_string($key)) {
$this->registerJsVar($key, $value, self::DEFAULT_POSITION_JS_VARIABLE);
} else {
$this->registerJsVarByConfig($value);
}
}
}

Expand All @@ -596,6 +613,68 @@ public function setTitle(string $value): void
$this->title = $value;
}

/**
* @throws InvalidArgumentException
*/
private function registerJsStringByConfig(?string $key, array $config): void
{
if (!array_key_exists(0, $config)) {
throw new InvalidArgumentException('Do not set JS string.');
}
$js = $config[0];

if (!is_string($js) && !($js instanceof Script)) {
throw new InvalidArgumentException(
sprintf(
'JS string should be string or instance of \\' . Script::class . '. Got %s.',
$this->getType($js),
)
);
}

$position = $config[1] ?? self::DEFAULT_POSITION_JS_STRING;
if (!$this->isValidPosition($position)) {
throw new InvalidArgumentException('Invalid position of JS strings.');
}

is_string($js)
? $this->registerJs($js, $position, $key)
: $this->registerScriptTag($js, $position, $key);
}

/**
* @throws InvalidArgumentException
*/
private function registerJsVarByConfig(array $config): void
{
if (!array_key_exists(0, $config)) {
throw new InvalidArgumentException('Do not set JS variable name.');
}
$key = $config[0];

if (!is_string($key)) {
throw new InvalidArgumentException(
sprintf(
'JS variable name should be string. Got %s.',
$this->getType($key),
)
);
}

if (!array_key_exists(1, $config)) {
throw new InvalidArgumentException('Do not set JS variable value.');
}
/** @var mixed */
$value = $config[1];

$position = $config[2] ?? self::DEFAULT_POSITION_JS_VARIABLE;
if (!$this->isValidPosition($position)) {
throw new InvalidArgumentException('Invalid position of JS variable.');
}

$this->registerJsVar($key, $value, $position);
}

/**
* @param Script[]|string[] $items
*/
Expand Down Expand Up @@ -633,4 +712,32 @@ private function generateJsWithoutTag(array $items): string
}
return implode("\n", $js);
}

/**
* @param mixed $position
*
* @psalm-assert int $position
*/
private function isValidPosition($position): bool
{
return in_array(
$position,
[
self::POSITION_HEAD,
self::POSITION_BEGIN,
self::POSITION_END,
self::POSITION_READY,
self::POSITION_LOAD,
],
true,
);
}

/**
* @param mixed $value
*/
private function getType($value): string
{
return is_object($value) ? get_class($value) : gettype($value);
}
}
78 changes: 78 additions & 0 deletions tests/WebViewTest.php
Expand Up @@ -4,6 +4,7 @@

namespace Yiisoft\View\Tests;

use InvalidArgumentException;
use Yiisoft\Files\FileHelper;
use Yiisoft\Html\Html;
use Yiisoft\View\WebView;
Expand Down Expand Up @@ -186,4 +187,81 @@ public function testRegisterJsAndRegisterScriptTagWithAjax(): void
$html
);
}

public function testSetJsStrings(): void
{
$this->webView->setJsStrings([
'uniqueName' => 'app1.start();',
'app2.start();',
'uniqueName2' => ['app3.start();', WebView::POSITION_BEGIN],
['app4.start();', WebView::POSITION_HEAD],
Html::script('{"@type":"Article"}')->type('application/ld+json'),
]);

$html = $this->webView->render('//rawlayout.php', ['content' => '']);

$expected = '1<script>app4.start();</script>2<script>app3.start();</script>3<script>app1.start();' . "\n" .
'app2.start();</script>' . "\n" .
'<script type="application/ld+json">{"@type":"Article"}</script>4';

$this->assertEqualsWithoutLE($expected, $html);
}

public function dataFailSetJsStrings(): array
{
return [
['Do not set JS string.', [[]]],
['Do not set JS string.', ['key' => []]],
['JS string should be string or instance of \Yiisoft\Html\Tag\Script. Got integer.', [[42]]],
['JS string should be string or instance of \Yiisoft\Html\Tag\Script. Got integer.', ['key' => [42]]],
['Invalid position of JS strings.', [['alert(1);', 99]]],
['Invalid position of JS strings.', ['key' => ['alert(1);', 99]]],
];
}

/**
* @dataProvider dataFailSetJsStrings
*/
public function testFailSetJsStrings(string $message, array $jsStrings): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage($message);
$this->webView->setJsStrings($jsStrings);
}

public function testSetJsVars(): void
{
$this->webView->setJsVars([
'var1' => 'value1',
'var2' => [1, 2],
['var3', 'value3', WebView::POSITION_END],
]);

$html = $this->webView->render('//rawlayout.php', ['content' => '']);

$expected = '1<script>var var1 = "value1";' . "\n" .
'var var2 = [1,2];</script>23<script>var var3 = "value3";</script>4';

$this->assertEqualsWithoutLE($expected, $html);
}

public function dataFailSetJsVars(): array
{
return [
['Do not set JS variable name.', [[]]],
['JS variable name should be string. Got integer.', [[42]]],
['Do not set JS variable value.', [['var']]],
['Invalid position of JS variable.', [['title', 'hello', 99]]],
];
}

/**
* @dataProvider dataFailSetJsVars
*/
public function testFailSetJsVars(string $message, array $jsVars): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage($message);
$this->webView->setJsVars($jsVars);
}
}

0 comments on commit acff1ba

Please sign in to comment.