From ae02253b4e7672fb64707be188fec0b9f4294a08 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Wed, 7 Feb 2024 10:33:16 +0000 Subject: [PATCH] refactor --- .../src/Components/Concerns/HasState.php | 23 ++++ .../forms/src/Components/MorphToSelect.php | 27 +--- .../forms/src/Concerns/CanBeValidated.php | 24 +++- packages/forms/src/Concerns/HasState.php | 12 +- tests/src/Forms/StateTest.php | 117 ++++++++++++++++++ 5 files changed, 169 insertions(+), 34 deletions(-) diff --git a/packages/forms/src/Components/Concerns/HasState.php b/packages/forms/src/Components/Concerns/HasState.php index 83d36e5d28..a2bf03b604 100644 --- a/packages/forms/src/Components/Concerns/HasState.php +++ b/packages/forms/src/Components/Concerns/HasState.php @@ -36,6 +36,8 @@ trait HasState protected bool | Closure $isDehydrated = true; + protected bool | Closure $isDehydratedWhenHidden = false; + protected ?string $statePath = null; protected string $cachedAbsoluteStatePath; @@ -135,6 +137,13 @@ trait HasState return $this; } + public function dehydratedWhenHidden(bool | Closure $condition = true): static + { + $this->isDehydratedWhenHidden = $condition; + + return $this; + } + public function formatStateUsing(?Closure $callback): static { $this->afterStateHydrated(fn (Component $component) => $component->state($component->evaluate($callback))); @@ -432,6 +441,20 @@ trait HasState return (bool) $this->evaluate($this->isDehydrated); } + public function isDehydratedWhenHidden(): bool + { + return (bool) $this->evaluate($this->isDehydratedWhenHidden); + } + + public function isHiddenAndNotDehydrated(): bool + { + if (! $this->isHidden()) { + return false; + } + + return ! $this->isDehydratedWhenHidden(); + } + public function getGetCallback(): Get { return new Get($this); diff --git a/packages/forms/src/Components/MorphToSelect.php b/packages/forms/src/Components/MorphToSelect.php index 43cbd356cd..dc1fb618f9 100644 --- a/packages/forms/src/Components/MorphToSelect.php +++ b/packages/forms/src/Components/MorphToSelect.php @@ -78,8 +78,9 @@ class MorphToSelect extends Component ->options($selectedType?->getOptionsUsing) ->getSearchResultsUsing($selectedType?->getSearchResultsUsing) ->getOptionLabelUsing($selectedType?->getOptionLabelUsing) - ->required($isRequired) - ->hidden(! $selectedType) + ->required(filled($selectedType)) + ->hidden(blank($selectedType)) + ->dehydratedWhenHidden() ->searchable($this->isSearchable()) ->searchDebounce($this->getSearchDebounce()) ->searchPrompt($this->getSearchPrompt()) @@ -99,28 +100,6 @@ class MorphToSelect extends Component ]; } - /** - * @param array $state - */ - public function dehydrateState(array &$state, bool $isDehydrated = true): void - { - parent::dehydrateState($state, $isDehydrated); - - if ($this->isRequired()) { - return; - } - - $relationship = $this->getRelationship(); - $typeColumn = $relationship->getMorphType(); - $keyColumn = $relationship->getForeignKeyName(); - - $statePath = $this->getStatePath(); - - if (blank(data_get($state, "{$statePath}.{$typeColumn}"))) { - data_set($state, "{$statePath}.{$keyColumn}", null); - } - } - public function optionsLimit(int | Closure $limit): static { $this->optionsLimit = $limit; diff --git a/packages/forms/src/Concerns/CanBeValidated.php b/packages/forms/src/Concerns/CanBeValidated.php index cea68f3e60..9fbfa6afe6 100644 --- a/packages/forms/src/Concerns/CanBeValidated.php +++ b/packages/forms/src/Concerns/CanBeValidated.php @@ -3,6 +3,7 @@ namespace Filament\Forms\Concerns; use Filament\Forms\Components; +use Filament\Forms\Components\Component; trait CanBeValidated { @@ -13,7 +14,11 @@ trait CanBeValidated { $attributes = []; - foreach ($this->getComponents() as $component) { + foreach ($this->getComponents(withHidden: true) as $component) { + if ($component->isHiddenAndNotDehydrated()) { + continue; + } + if ($component instanceof Components\Contracts\HasValidationRules) { $component->dehydrateValidationAttributes($attributes); } @@ -40,7 +45,11 @@ trait CanBeValidated { $messages = []; - foreach ($this->getComponents() as $component) { + foreach ($this->getComponents(withHidden: true) as $component) { + if ($component->isHiddenAndNotDehydrated()) { + continue; + } + if ($component instanceof Components\Contracts\HasValidationRules) { $component->dehydrateValidationMessages($messages); } @@ -67,7 +76,11 @@ trait CanBeValidated { $rules = []; - foreach ($this->getComponents() as $component) { + foreach ($this->getComponents(withHidden: true) as $component) { + if ($component->isHiddenAndNotDehydrated()) { + continue; + } + if ($component instanceof Components\Contracts\HasValidationRules) { $component->dehydrateValidationRules($rules); } @@ -92,7 +105,10 @@ trait CanBeValidated */ public function validate(): array { - if (! count($this->getComponents())) { + if (! count(array_filter( + $this->getComponents(withHidden: true), + fn (Component $component): bool => ! $component->isHiddenAndNotDehydrated(), + ))) { return []; } diff --git a/packages/forms/src/Concerns/HasState.php b/packages/forms/src/Concerns/HasState.php index e7a09b9eda..c503d55265 100644 --- a/packages/forms/src/Concerns/HasState.php +++ b/packages/forms/src/Concerns/HasState.php @@ -70,8 +70,8 @@ trait HasState */ public function dehydrateState(array &$state = [], bool $isDehydrated = true): array { - foreach ($this->getComponents() as $component) { - if ($component->isHidden()) { + foreach ($this->getComponents(withHidden: true) as $component) { + if ($component->isHiddenAndNotDehydrated()) { continue; } @@ -87,8 +87,8 @@ trait HasState */ public function mutateDehydratedState(array &$state = []): array { - foreach ($this->getComponents() as $component) { - if ($component->isHidden()) { + foreach ($this->getComponents(withHidden: true) as $component) { + if ($component->isHiddenAndNotDehydrated()) { continue; } @@ -130,8 +130,8 @@ trait HasState */ public function mutateStateForValidation(array &$state = []): array { - foreach ($this->getComponents() as $component) { - if ($component->isHidden()) { + foreach ($this->getComponents(withHidden: true) as $component) { + if ($component->isHiddenAndNotDehydrated()) { continue; } diff --git a/tests/src/Forms/StateTest.php b/tests/src/Forms/StateTest.php index ff78097381..0171cb9399 100644 --- a/tests/src/Forms/StateTest.php +++ b/tests/src/Forms/StateTest.php @@ -440,6 +440,123 @@ test('components can be excluded from state dehydration if their parent componen ->dehydrateState()->toBe([]); }); +test('hidden components are excluded from state dehydration', function () { + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component()) + ->statePath(Str::random()) + ->default(Str::random()) + ->hidden(), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->toBe([]); +}); + +test('hidden components are excluded from state dehydration if their parent component is', function () { + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component()) + ->hidden() + ->schema([ + (new Component()) + ->statePath(Str::random()) + ->default(Str::random()), + ]), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->toBe([]); +}); + +test('hidden components are excluded from state dehydration except if they are marked as dehydrated', function () { + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component()) + ->statePath(Str::random()) + ->default(Str::random()) + ->hidden() + ->dehydratedWhenHidden(), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->not()->toBe([]); +}); + +test('disabled components are excluded from state dehydration', function () { + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component()) + ->statePath(Str::random()) + ->default(Str::random()) + ->disabled(), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->toBe([]); +}); + +test('disabled components are excluded from state dehydration if their parent component is', function () { + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component()) + ->disabled() + ->schema([ + (new Component()) + ->statePath(Str::random()) + ->default(Str::random()), + ]), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->toBe([]); +}); + +test('disabled components are excluded from state dehydration except if they are marked as dehydrated', function () { + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component()) + ->statePath(Str::random()) + ->default(Str::random()) + ->disabled() + ->dehydrated(), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->not()->toBe([]); +}); + +test('disabled components are excluded from state dehydration if their parent component is disabled and not marked as dehydrated', function () { + $container = ComponentContainer::make(Livewire::make()) + ->statePath('data') + ->components([ + (new Component()) + ->disabled() + ->schema([ + (new Component()) + ->statePath(Str::random()) + ->default(Str::random()) + ->dehydrated(), + ]), + ]) + ->fill(); + + expect($container) + ->dehydrateState()->toBe([]); +}); + test('dehydrated state can be mutated', function () { $container = ComponentContainer::make(Livewire::make()) ->statePath('data')