Skip to content
This repository has been archived by the owner on Feb 27, 2024. It is now read-only.

Commit

Permalink
Added test + improved Vue props object merger
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalbaljet committed Dec 20, 2023
1 parent d8b898c commit f920f04
Show file tree
Hide file tree
Showing 22 changed files with 211 additions and 44 deletions.
50 changes: 50 additions & 0 deletions app/app/View/Components/ToVueProp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace App\View\Components;

use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
use ProtoneMedia\SpladeCore\Attributes\VueProp;

class ToVueProp extends Component
{
#[VueProp]
public mixed $mixed = 'foo';

#[VueProp]
public string $string = 'foo';

#[VueProp]
public string $defaultString = 'foo';

#[VueProp]
public ?string $nullableString = null;

/**
* Create a new component instance.
*/
public function __construct(
#[VueProp] public int $int,
#[VueProp] public bool $bool,
#[VueProp] public array $array,
#[VueProp] public object $object,
#[VueProp] public ?int $nullableInt = null,
#[VueProp] public ?bool $nullableBool = null,
#[VueProp] public ?array $nullableArray = null,
#[VueProp] public ?object $nullableObject = null,
#[VueProp] public int $defaultInt = 1,
#[VueProp] public bool $defaultBool = true,
#[VueProp] public array $defaultArray = ['foo'],
#[VueProp] public array|bool|string $multipleTypes = ['foo'],
) {
}

/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.to-vue-prop');
}
}
19 changes: 19 additions & 0 deletions app/resources/views/components/to-vue-prop.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script setup></script>
<div>
<p>Mixed: <span v-text="mixed" /></p>
<p>String: <span v-text="string" /></p>
<p>Default String: <span v-text="defaultString" /></p>
<p>Nullable String: <span v-text="nullableString" /></p>
<p>Int: <span v-text="int" /></p>
<p>Bool: <span v-text="bool" /></p>
<p>Array: <span v-text="array" /></p>
<p>Object: <span v-text="object" /></p>
<p>Nullable Int: <span v-text="nullableInt" /></p>
<p>Nullable Bool: <span v-text="nullableBool" /></p>
<p>Nullable Array: <span v-text="nullableArray" /></p>
<p>Nullable Object: <span v-text="nullableObject" /></p>
<p>Default Int: <span v-text="defaultInt" /></p>
<p>Default Bool: <span v-text="defaultBool" /></p>
<p>Default Array: <span v-text="defaultArray" /></p>
<p>Multiple Types: <span v-text="multipleTypes" /></p>
</div>
8 changes: 8 additions & 0 deletions app/resources/views/to-vue-prop.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<x-layout>
<x-to-vue-prop
:int="1"
:bool="false"
:array="['foo', 'bar']"
:object="(object) ['foo' => 'bar']"
/>
</x-layout>
1 change: 1 addition & 0 deletions app/routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
Route::view('/refresh', 'refresh')->middleware(Refreshable::class);
Route::view('/refresh-state', 'refresh-state')->middleware(Refreshable::class);
Route::view('/regular-view', 'regular-view');
Route::view('/to-vue-prop', 'to-vue-prop');
Route::view('/two-way-binding', 'two-way-binding');

Route::get('/login', function () {
Expand Down
33 changes: 33 additions & 0 deletions app/tests/Browser/ToVuePropTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Tests\Browser;

use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class ToVuePropTest extends DuskTestCase
{
/** @test */
public function it_can_pass_blade_props_as_vue_rops()
{
$this->browse(function (Browser $browser) {
$browser->visit('/to-vue-prop')
->waitForText('Splade Core demo app')
->assertSee('Mixed: foo')
->assertSee('String: foo')
->assertSee('Default String: foo')
->assertSee('Nullable String:')
->assertSee('Int: 1')
->assertSee('Bool: false')
->assertSee('Array: { "foo": "bar" }')
->assertSee('Object: { "foo": "bar" }')
->assertSee('Nullable Int:')
->assertSee('Nullable Bool:')
->assertSee('Nullable Array:')
->assertSee('Nullable Object:')
->assertSee('Default Int: 1')
->assertSee('Default Bool: true')
->assertSee('Default Array: [ "foo" ]');
});
}
}
1 change: 1 addition & 0 deletions app/tests/Unit/Console/BuildComponentsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function it_builds_the_components()
'ComponentTwoWayBinding',
'IncludedView',
'RegularView',
'ComponentToVueProp',
] as $component) {
$this->assertFileExists(resource_path('js/splade/Splade'.$component.'.vue'));
$this->assertMatchesVueSnapshot($filesytem->get(resource_path('js/splade/Splade'.$component.'.vue')));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { GenericSpladeComponent } from '@protonemedia/laravel-splade-core'
import { computed, h, ref } from 'vue'
const props = defineProps(['spladeBridge', 'spladeTemplateId'])
const props = defineProps({ spladeBridge: Object, spladeTemplateId: String })
const message = ref('Hello Vue!')
const reversed = computed(() => message.value.split('').reverse().join(''))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup>
import { h, ref } from 'vue'
const props = defineProps(['spladeTemplateId'])
const props = defineProps({ spladeTemplateId: String })
const message = ref('Hello Included view!')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup>
import { h, ref } from 'vue'
const props = defineProps(['spladeTemplateId'])
const props = defineProps({ spladeTemplateId: String })
const message = ref('Hello World!')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script setup>
import { GenericSpladeComponent } from '@protonemedia/laravel-splade-core'
import { h } from 'vue'
const props = defineProps({
spladeBridge: Object,
spladeTemplateId: String,
mixed: { default: 'foo' },
string: { type: String, default: 'foo' },
defaultString: { type: String, default: 'foo' },
nullableString: { type: String, default: null },
int: { type: Number, default: null },
bool: { type: Boolean, default: null },
array: { type: Array, default: null },
object: { type: Object, default: null },
nullableInt: { type: Number, default: null },
nullableBool: { type: Boolean, default: null },
nullableArray: { type: Array, default: null },
nullableObject: { type: Object, default: null },
defaultInt: { type: Number, default: 1 },
defaultBool: { type: Boolean, default: true },
defaultArray: { type: Array, default: JSON.parse('[\u0022foo\u0022]') },
multipleTypes: { type: [Array, String, Boolean], default: JSON.parse('[\u0022foo\u0022]') },
})
const spladeRender = h({
name: 'SpladeComponentToVuePropRender',
components: { GenericSpladeComponent },
template: spladeTemplates[props.spladeTemplateId],
data: () => {
return { ...props }
},
})
</script>
<template><spladeRender /></template>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { BladeComponent, GenericSpladeComponent } from '@protonemedia/laravel-splade-core'
import { h, ref } from 'vue'
const props = defineProps(['spladeBridge', 'spladeTemplateId'])
const props = defineProps({ spladeBridge: Object, spladeTemplateId: String })
const _spladeBridgeState = ref(props.spladeBridge)
const execute = BladeComponent.asyncComponentMethod('execute', _spladeBridgeState)
const sleep = BladeComponent.asyncComponentMethod('sleep', _spladeBridgeState)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { BladeComponent, GenericSpladeComponent } from '@protonemedia/laravel-splade-core'
import { h, ref } from 'vue'
const props = defineProps(['spladeBridge', 'spladeTemplateId'])
const props = defineProps({ spladeBridge: Object, spladeTemplateId: String })
const _spladeBridgeState = ref(props.spladeBridge)
const execute = BladeComponent.asyncComponentMethod('execute', _spladeBridgeState)
const fail = BladeComponent.asyncComponentMethod('fail', _spladeBridgeState)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { BladeComponent, GenericSpladeComponent } from '@protonemedia/laravel-splade-core'
import { computed, h, ref } from 'vue'
const props = defineProps(['spladeBridge', 'spladeTemplateId'])
const props = defineProps({ spladeBridge: Object, spladeTemplateId: String })
const _spladeBridgeState = ref(props.spladeBridge)
const setMessage = BladeComponent.asyncComponentMethod('setMessage', _spladeBridgeState)
const message = computed({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { GenericSpladeComponent } from '@protonemedia/laravel-splade-core'
import { h, onMounted } from 'vue'
const props = defineProps(['spladeBridge', 'spladeTemplateId', 'modelValue'])
const props = defineProps({ spladeBridge: Object, spladeTemplateId: String, modelValue: {} })
const $refs = {}
const setSpladeRef = (key, value) => ($refs[key] = value)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { GenericSpladeComponent } from '@protonemedia/laravel-splade-core'
import { h, ref } from 'vue'
const props = defineProps(['spladeBridge', 'spladeTemplateId'])
const props = defineProps({ spladeBridge: Object, spladeTemplateId: String })
const form = ref({
package: 'Splade',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { BladeComponent, GenericSpladeComponent } from '@protonemedia/laravel-splade-core'
import { h, inject, ref } from 'vue'
const props = defineProps(['spladeBridge', 'spladeTemplateId'])
const props = defineProps({ spladeBridge: Object, spladeTemplateId: String })
const _spladeBridgeState = ref(props.spladeBridge)
const _spladeTemplateBus = inject('$spladeTemplateBus')
const refreshComponent = BladeComponent.asyncRefreshComponent(_spladeBridgeState, _spladeTemplateBus)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { BladeComponent, GenericSpladeComponent } from '@protonemedia/laravel-splade-core'
import { h, inject, ref } from 'vue'
const props = defineProps(['spladeBridge', 'spladeTemplateId'])
const props = defineProps({ spladeBridge: Object, spladeTemplateId: String })
const _spladeBridgeState = ref(props.spladeBridge)
const _spladeTemplateBus = inject('$spladeTemplateBus')
const refreshComponent = BladeComponent.asyncRefreshComponent(_spladeBridgeState, _spladeTemplateBus)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { GenericSpladeComponent } from '@protonemedia/laravel-splade-core'
import { computed, h, ref } from 'vue'
const props = defineProps(['spladeBridge', 'spladeTemplateId'])
const props = defineProps({ spladeBridge: Object, spladeTemplateId: String })
const message = ref('Hello Vue!')
const uppercase = computed(() => message.value.toUpperCase())
Expand Down
8 changes: 4 additions & 4 deletions app/tests/Unit/ScriptParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public function it_can_merge_the_props_when_no_props_are_defined()

$this->assertEquals([
'original' => '',
'new' => "const props = defineProps(['foo']);",
'new' => 'const props = defineProps({foo: String});',
], $parser->getDefineProps([
'foo' => 'String',
]));
Expand All @@ -31,7 +31,7 @@ public function it_can_extract_the_define_props_when_called_with_an_array()

$this->assertEquals([
'original' => "defineProps(['foo', 'bar']);",
'new' => "const props = defineProps(['foo', 'bar']);",
'new' => 'const props = defineProps({foo: {}, bar: {}});',
], $parser->getDefineProps());
}

Expand All @@ -46,7 +46,7 @@ public function it_can_extract_the_define_props_when_called_with_an_array_with_a

$this->assertEquals([
'original' => "const props = defineProps(['foo', 'bar']);",
'new' => "const props = defineProps(['foo', 'bar']);",
'new' => 'const props = defineProps({foo: {}, bar: {}});',
], $parser->getDefineProps());
}

Expand All @@ -61,7 +61,7 @@ public function it_can_extract_the_define_props_when_called_with_an_array_and_me

$this->assertEquals([
'original' => "defineProps(['foo', 'bar']);",
'new' => "const props = defineProps(['foo', 'bar', 'baz']);",
'new' => 'const props = defineProps({foo: {}, bar: {}, baz: String});',
], $parser->getDefineProps([
'baz' => 'String',
]));
Expand Down
4 changes: 3 additions & 1 deletion src/BladeViewExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,9 @@ protected function extractDefinePropsFromScript(): array
$type = is_array($specs->type) ? '['.implode(',', $specs->type).']' : "{$specs->type}";
$default = Js::from($specs->default)->toHtml();

return "{type: {$type}, default: {$default}}";
return $type
? "{type: {$type}, default: {$default}}"
: "{default: {$default}}";
});

$defaultProps = Collection::make(['spladeTemplateId' => 'String'])
Expand Down
24 changes: 16 additions & 8 deletions src/ComponentSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,9 @@ public function getPropsFromComponent(): array
continue;
}

$value = $property->isInitialized($this->component) ? $property->getValue($this->component) : null;
$value = $property->isInitialized($this->component)
? $property->getValue($this->component)
: null;

if ($value instanceof Model) {
$value = (object) $value->jsonSerialize();
Expand All @@ -250,9 +252,12 @@ public function getPropsFromComponent(): array
$value = $value->jsonSerialize();
}

$defaultValue = $property->getDefaultValue();
$defaultValue = $property->hasDefaultValue()
? $property->getDefaultValue()
: null;

$constructorParameter = collect($constructorParameters)->first(fn ($parameter) => $parameter->getName() === $name);
$constructorParameter = collect($constructorParameters)
->first(fn ($parameter) => $parameter->getName() === $name);

if ($constructorParameter?->isDefaultValueAvailable()) {
$defaultValue = $constructorParameter->getDefaultValue();
Expand All @@ -271,22 +276,25 @@ public function getPropsFromComponent(): array
/**
* Maps a PHP type to a Vue type.
*/
public static function mapTypeToVueType(?ReflectionType $type = null): array|string
public static function mapTypeToVueType(?ReflectionType $type = null): array|string|null
{
if ($type instanceof \ReflectionUnionType) {
return collect($type->getTypes())->map(fn ($type) => static::mapTypeToVueType($type))->all();
$types = collect($type->getTypes())
->map(fn ($type = null) => static::mapTypeToVueType($type))
->filter();

return $types->isEmpty() ? null : $types->all();
}

return match ($type->getName()) {
return match ($type?->getName()) {
'bool' => 'Boolean',
'int' => 'Number',
'float' => 'Number',
'string' => 'String',
'array' => 'Array',
'object' => 'Object',
'null' => 'Null',
'mixed' => 'Any',
default => 'Any',
default => null,
};
}

Expand Down
Loading

0 comments on commit f920f04

Please sign in to comment.