diff --git a/composer.lock b/composer.lock index 06fe051952..760aafaadd 100644 --- a/composer.lock +++ b/composer.lock @@ -127,16 +127,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.241.1", + "version": "3.242.0", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "124ec1258f667e99f2fc3288f3e59fd4903a22d3" + "reference": "cab6d4f830ce275a1742a3e35e2625917b006dd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/124ec1258f667e99f2fc3288f3e59fd4903a22d3", - "reference": "124ec1258f667e99f2fc3288f3e59fd4903a22d3", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/cab6d4f830ce275a1742a3e35e2625917b006dd2", + "reference": "cab6d4f830ce275a1742a3e35e2625917b006dd2", "shasum": "" }, "require": { @@ -215,9 +215,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.241.1" + "source": "https://github.com/aws/aws-sdk-php/tree/3.242.0" }, - "time": "2022-11-09T19:31:43+00:00" + "time": "2022-11-10T19:15:37+00:00" }, { "name": "blade-ui-kit/blade-heroicons", diff --git a/packages/forms/src/Components/BaseFileUpload.php b/packages/forms/src/Components/BaseFileUpload.php index 12da47d775..d411ae1890 100644 --- a/packages/forms/src/Components/BaseFileUpload.php +++ b/packages/forms/src/Components/BaseFileUpload.php @@ -118,7 +118,7 @@ class BaseFileUpload extends Field if ($component->getVisibility() === 'private') { try { - $url ??= $storage->temporaryUrl( + $url = $storage->temporaryUrl( $file, now()->addMinutes(5), ); diff --git a/packages/spatie-laravel-media-library-plugin/src/Forms/Components/SpatieMediaLibraryFileUpload.php b/packages/spatie-laravel-media-library-plugin/src/Forms/Components/SpatieMediaLibraryFileUpload.php index 3467a4d469..4de3b143c2 100644 --- a/packages/spatie-laravel-media-library-plugin/src/Forms/Components/SpatieMediaLibraryFileUpload.php +++ b/packages/spatie-laravel-media-library-plugin/src/Forms/Components/SpatieMediaLibraryFileUpload.php @@ -77,7 +77,7 @@ class SpatieMediaLibraryFileUpload extends FileUpload if ($component->getVisibility() === 'private') { try { - $url ??= $media?->getTemporaryUrl( + $url = $media?->getTemporaryUrl( now()->addMinutes(5), ); } catch (Throwable $exception) { @@ -92,9 +92,9 @@ class SpatieMediaLibraryFileUpload extends FileUpload $url ??= $media?->getUrl(); return [ - 'name' => $media->name ?? $media->file_name, - 'size' => $media->size, - 'type' => $media->mime_type, + 'name' => $media->name ?? $media->getAttributeValue('file_name'), + 'size' => $media->getAttributeValue('size'), + 'type' => $media->getAttributeValue('mime_type'), 'url' => $url, ]; }); diff --git a/packages/tables/.stubs.php b/packages/tables/.stubs.php index 7cfeaeede4..5e66ef6da2 100644 --- a/packages/tables/.stubs.php +++ b/packages/tables/.stubs.php @@ -109,6 +109,12 @@ namespace Livewire\Testing { public function assertTableColumnStateNotSet(string $name, $value, $record): static {} + public function assertTableColumnSummarizerExists(string $columnName, string $summarizerId): static {} + + public function assertTableColumnSummarySet(string $columnName, string $summarizerId, $state, bool $isCurrentPaginationPageOnly = false): static {} + + public function assertTableColumnSummaryNotSet(string $columnName, string $summarizerId, $state, bool $isCurrentPaginationPageOnly = false): static {} + public function sortTable(?string $name = null, ?string $direction = null): static {} public function searchTable(?string $search = null): static {} diff --git a/packages/tables/resources/views/components/summary/index.blade.php b/packages/tables/resources/views/components/summary/index.blade.php index f61a75e421..b46b7ba542 100644 --- a/packages/tables/resources/views/components/summary/index.blade.php +++ b/packages/tables/resources/views/components/summary/index.blade.php @@ -50,12 +50,7 @@ :columns="$columns" :heading="__('filament-tables::table.summary.subheadings.page', ['label' => $pluralModelLabel])" :is-selection-enabled="$isSelectionEnabled" - :query="$this->applySortingToTableQuery( - $this->getFilteredTableQuery()->forPage( - page: $records->currentPage(), - perPage: $records->perPage(), - ), - )" + :query="$this->getPageTableSummaryQuery()" /> @endif @@ -65,5 +60,5 @@ :columns="$columns" :heading="__('filament-tables::table.summary.subheadings.all', ['label' => $pluralModelLabel])" :is-selection-enabled="$isSelectionEnabled" - :query="$this->getFilteredTableQuery()" + :query="$this->getAllTableSummaryQuery()" /> diff --git a/packages/tables/src/Columns/Concerns/CanBeSummarized.php b/packages/tables/src/Columns/Concerns/CanBeSummarized.php index fdd4a21a38..c9c387947a 100644 --- a/packages/tables/src/Columns/Concerns/CanBeSummarized.php +++ b/packages/tables/src/Columns/Concerns/CanBeSummarized.php @@ -12,10 +12,15 @@ trait CanBeSummarized public function summarize(array | Summarizer $summarizers): static { - $this->summarizers = array_merge( - $this->summarizers, - Arr::wrap($summarizers), - ); + foreach (Arr::wrap($summarizers) as $summarizer) { + $summarizer->column($this); + + if (filled($id = $summarizer->getId())) { + $this->summarizers[$id] = $summarizer; + } else { + $this->summarizers[] = $summarizer; + } + } return $this; } @@ -23,13 +28,17 @@ trait CanBeSummarized public function getSummary(Builder $query): array { return array_map(function (Summarizer $summarizer) use ($query): Summarizer { - $summarizer->column($this); $summarizer->query($query); return $summarizer; }, $this->getSummarizers()); } + public function getSummarizer(string $id): ?Summarizer + { + return $this->getSummarizers()[$id] ?? null; + } + public function getSummarizers(): array { return $this->summarizers; @@ -37,6 +46,6 @@ trait CanBeSummarized public function hasSummary(): bool { - return count($this->summarizers); + return (bool) count($this->summarizers); } } diff --git a/packages/tables/src/Columns/Summarizers/Summarizer.php b/packages/tables/src/Columns/Summarizers/Summarizer.php index 8cbd943af4..f5982a7c0f 100644 --- a/packages/tables/src/Columns/Summarizers/Summarizer.php +++ b/packages/tables/src/Columns/Summarizers/Summarizer.php @@ -21,16 +21,30 @@ class Summarizer extends ViewComponent protected string $view = 'filament-tables::columns.summaries.text'; + protected ?string $id = null; + protected ?Closure $using = null; - public static function make(): static + final public function __construct(?string $id = null) { - $static = app(static::class); + $this->id($id); + } + + public static function make(?string $id = null): static + { + $static = app(static::class, ['id' => $id]); $static->configure(); return $static; } + public function id(?string $id): static + { + $this->id = $id; + + return $this; + } + public function using(?Closure $using): static { $this->using = $using; @@ -52,7 +66,7 @@ class Summarizer extends ViewComponent ->plural() ->camel(); - $query = $relationship->getModel()->newQuery() + $query = $relationship->getQuery()->getModel()->newQuery() ->whereHas( $inverseRelationship, fn (EloquentBuilder $relatedQuery): EloquentBuilder => $this->hasPaginatedQuery() ? @@ -61,7 +75,7 @@ class Summarizer extends ViewComponent ); } - $query = DB::table($query); + $query = DB::table($query->getQuery()); if ($this->hasQueryModificationCallback()) { $query = $this->evaluate($this->modifyQueryUsing, [ @@ -85,6 +99,11 @@ class Summarizer extends ViewComponent return null; } + public function getId(): ?string + { + return $this->id; + } + protected function hasPaginatedQuery(): bool { return $this->getQuery()->getQuery()->limit !== null; diff --git a/packages/tables/src/Columns/TextColumn.php b/packages/tables/src/Columns/TextColumn.php index 65b7ab4815..c717613e5c 100644 --- a/packages/tables/src/Columns/TextColumn.php +++ b/packages/tables/src/Columns/TextColumn.php @@ -44,6 +44,8 @@ class TextColumn extends Column ) { $this->formatStateUsing(static fn ($state): ?string => $enum::tryFrom($state)?->getLabel() ?? ($default ?? $state)); } + + return $this; } public function rowIndex(bool $isFromZero = false): static diff --git a/packages/tables/src/Concerns/CanSortRecords.php b/packages/tables/src/Concerns/CanSortRecords.php index ab6ab24cfd..1bf754f59c 100644 --- a/packages/tables/src/Concerns/CanSortRecords.php +++ b/packages/tables/src/Concerns/CanSortRecords.php @@ -46,14 +46,17 @@ trait CanSortRecords protected function applySortingToTableQuery(Builder $query): Builder { if ($this->isTableReordering()) { - return $query->orderBy($this->getTableReorderColumn()); + return $query->orderBy($this->getTable()->getReorderColumn()); } $sortColumn = $this->tableSortColumn; if (! $sortColumn) { + /** @phpstan-ignore-next-line */ + $orders = $query->getQuery()->orders ?? []; + return $query->when( - count($query->getQuery()->orders ?? []) === 0, + count($orders) === 0, fn (Builder $query) => $query->orderBy($query->getModel()->getQualifiedKeyName()), ); } diff --git a/packages/tables/src/Concerns/CanSummarizeRecords.php b/packages/tables/src/Concerns/CanSummarizeRecords.php new file mode 100644 index 0000000000..50b07531df --- /dev/null +++ b/packages/tables/src/Concerns/CanSummarizeRecords.php @@ -0,0 +1,23 @@ +getFilteredTableQuery(); + } + + public function getPageTableSummaryQuery(): Builder + { + return $this->applySortingToTableQuery( + $this->getFilteredTableQuery()->forPage( + page: $this->getTableRecords()->currentPage(), + perPage: $this->getTableRecords()->perPage(), + ), + ); + } +} diff --git a/packages/tables/src/Concerns/InteractsWithTable.php b/packages/tables/src/Concerns/InteractsWithTable.php index 57056eb253..af6ee1f1ac 100644 --- a/packages/tables/src/Concerns/InteractsWithTable.php +++ b/packages/tables/src/Concerns/InteractsWithTable.php @@ -13,26 +13,27 @@ use Illuminate\Database\Eloquent\Relations\Relation; trait InteractsWithTable { - use CanBeStriped; use CanPaginateRecords; - use CanPollRecords; use CanReorderRecords; use CanSearchRecords; use CanSelectRecords; use CanSortRecords; + use CanSummarizeRecords; use CanToggleColumns; use HasActions; use HasBulkActions; use HasColumns; + use HasFilters; + use HasRecords; + use Forms\Concerns\InteractsWithForms; + use CanBeStriped; + use CanPollRecords; use HasContent; use HasEmptyState; - use HasFilters; use HasHeader; - use HasRecords; use HasRecordAction; use HasRecordClasses; use HasRecordUrl; - use Forms\Concerns\InteractsWithForms; protected Table $table; diff --git a/packages/tables/src/Contracts/HasTable.php b/packages/tables/src/Contracts/HasTable.php index fd6fe93e13..b713a34af6 100644 --- a/packages/tables/src/Contracts/HasTable.php +++ b/packages/tables/src/Contracts/HasTable.php @@ -8,6 +8,7 @@ use Filament\Tables\Actions\Action; use Filament\Tables\Actions\BulkAction; use Filament\Tables\Table; use Illuminate\Contracts\Pagination\Paginator; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; @@ -51,6 +52,10 @@ interface HasTable extends HasForms public function getTableSortDirection(): ?string; + public function getAllTableSummaryQuery(): Builder; + + public function getPageTableSummaryQuery(): Builder; + public function isTableColumnToggledHidden(string $name): bool; public function getTableColumnToggleForm(): Form; diff --git a/packages/tables/src/TablesServiceProvider.php b/packages/tables/src/TablesServiceProvider.php index 63488fb19b..ccfc78e410 100644 --- a/packages/tables/src/TablesServiceProvider.php +++ b/packages/tables/src/TablesServiceProvider.php @@ -7,6 +7,7 @@ use Filament\Tables\Testing\TestsBulkActions; use Filament\Tables\Testing\TestsColumns; use Filament\Tables\Testing\TestsFilters; use Filament\Tables\Testing\TestsRecords; +use Filament\Tables\Testing\TestsSummaries; use Illuminate\Filesystem\Filesystem; use Livewire\Testing\TestableLivewire; use Spatie\LaravelPackageTools\Package; @@ -61,5 +62,6 @@ class TablesServiceProvider extends PackageServiceProvider TestableLivewire::mixin(new TestsColumns()); TestableLivewire::mixin(new TestsFilters()); TestableLivewire::mixin(new TestsRecords()); + TestableLivewire::mixin(new TestsSummaries()); } } diff --git a/packages/tables/src/Testing/TestsSummaries.php b/packages/tables/src/Testing/TestsSummaries.php new file mode 100644 index 0000000000..4277e23ae4 --- /dev/null +++ b/packages/tables/src/Testing/TestsSummaries.php @@ -0,0 +1,88 @@ +assertTableColumnSummarizerExists($columnName, $summarizerId); + + $summarizer = $this->instance()->getTable()->getColumn($columnName)->getSummarizer($summarizerId); + + $query = $isCurrentPaginationPageOnly ? + $this->instance()->getPageTableSummaryQuery() : + $this->instance()->getAllTableSummaryQuery(); + + $livewireClass = $this->instance()::class; + + Assert::assertEquals( + $state, + $summarizer->query($query)->getState(), + message: "Failed asserting that summarizer [$summarizerId], for column [{$columnName}], on the [{$livewireClass}] component, is set.", + ); + + return $this; + }; + } + + public function assertTableColumnSummaryNotSet(): Closure + { + return function (string $columnName, string $summarizerId, $state, bool $isCurrentPaginationPageOnly = false): static { + /** @phpstan-ignore-next-line */ + $this->assertTableColumnSummarizerExists($columnName, $summarizerId); + + $summarizer = $this->instance()->getTable()->getColumn($columnName)->getSummarizer($summarizerId); + + $query = $isCurrentPaginationPageOnly ? + $this->instance()->getPageTableSummaryQuery() : + $this->instance()->getAllTableSummaryQuery(); + + $livewireClass = $this->instance()::class; + + Assert::assertNotEquals( + $state, + $summarizer->query($query)->getState(), + message: "Failed asserting that summarizer [$summarizerId], for column [{$columnName}], on the [{$livewireClass}] component, is not set.", + ); + + return $this; + }; + } + + public function assertTableColumnSummarizerExists(): Closure + { + return function (string $columnName, string $summarizerId): static { + $this->assertTableColumnExists($columnName); + + $column = $this->instance()->getTable()->getColumn($columnName); + + $summarizer = $column->getSummarizer($summarizerId); + + $livewireClass = $this->instance()::class; + + Assert::assertInstanceOf( + Summarizer::class, + $summarizer, + message: "Failed asserting that a table column with name [{$columnName}] has a summarizer with ID [{$summarizerId}] on the [{$livewireClass}] component. Please ensure that the ID is passed to the summarizer with [Summarizer::make('{$summarizerId}')].", + ); + + return $this; + }; + } +} diff --git a/tests/database/migrations/create_posts_table.php b/tests/database/migrations/create_posts_table.php index 44522ca99b..bc7257c061 100644 --- a/tests/database/migrations/create_posts_table.php +++ b/tests/database/migrations/create_posts_table.php @@ -13,9 +13,9 @@ return new class() extends Migration $table->foreignId('author_id'); $table->text('content')->nullable(); $table->boolean('is_published')->default(true); + $table->unsignedTinyInteger('rating')->default(0); $table->json('tags')->nullable(); $table->string('title'); - $table->unsignedTinyInteger('rating')->default(0); $table->timestamps(); }); } diff --git a/tests/src/Tables/Fixtures/PostsTable.php b/tests/src/Tables/Fixtures/PostsTable.php index 63be1baa3b..43a82f17fa 100644 --- a/tests/src/Tables/Fixtures/PostsTable.php +++ b/tests/src/Tables/Fixtures/PostsTable.php @@ -8,7 +8,8 @@ use Filament\Tables; use Filament\Tables\Table; use Filament\Tests\Models\Post; use Illuminate\Contracts\View\View; -use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Builder as EloquentBuilder; +use Illuminate\Database\Query\Builder; use Livewire\Component; class PostsTable extends Component implements Tables\Contracts\HasTable @@ -30,8 +31,31 @@ class PostsTable extends Component implements Tables\Contracts\HasTable ->action( Tables\Actions\Action::make('column-action-object') ->action(fn () => $this->emit('column-action-object-called')), - ), - Tables\Columns\IconColumn::make('is_published')->boolean(), + ) + ->summarize([ + Tables\Columns\Summarizers\Range::make('range'), + Tables\Columns\Summarizers\Range::make('published_range') + ->query(fn (Builder $query) => $query->where('is_published', true)), + ]), + Tables\Columns\IconColumn::make('is_published') + ->boolean() + ->summarize([ + Tables\Columns\Summarizers\Count::make('published_count') + ->query(fn (Builder $query) => $query->where('is_published', true)), + ]), + Tables\Columns\TextColumn::make('rating') + ->summarize([ + Tables\Columns\Summarizers\Average::make('average'), + Tables\Columns\Summarizers\Average::make('published_average') + ->query(fn (Builder $query) => $query->where('is_published', true)), + Tables\Columns\Summarizers\Count::make('count'), + Tables\Columns\Summarizers\Range::make('range'), + Tables\Columns\Summarizers\Range::make('published_range') + ->query(fn (Builder $query) => $query->where('is_published', true)), + Tables\Columns\Summarizers\Sum::make('sum'), + Tables\Columns\Summarizers\Sum::make('published_sum') + ->query(fn (Builder $query) => $query->where('is_published', true)), + ]), Tables\Columns\TextColumn::make('visible'), Tables\Columns\TextColumn::make('hidden') ->hidden(), @@ -40,7 +64,7 @@ class PostsTable extends Component implements Tables\Contracts\HasTable ]) ->filters([ Tables\Filters\Filter::make('is_published') - ->query(fn (Builder $query) => $query->where('is_published', true)), + ->query(fn (EloquentBuilder $query) => $query->where('is_published', true)), Tables\Filters\SelectFilter::make('author') ->relationship('author', 'name'), Tables\Filters\SelectFilter::make('select_filter_attribute') diff --git a/tests/src/Tables/SummaryTest.php b/tests/src/Tables/SummaryTest.php new file mode 100644 index 0000000000..19a130c20a --- /dev/null +++ b/tests/src/Tables/SummaryTest.php @@ -0,0 +1,152 @@ +count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'average', $posts->avg('rating')); +}); + +it('can average values in a column on this pagination page', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'average', $posts->take(10)->avg('rating'), isCurrentPaginationPageOnly: true); +}); + +it('can average subset of values in a column', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'published_average', $posts->where('is_published', true)->avg('rating')); +}); + +it('can average subset of values in a column on this pagination page', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'published_average', $posts->take(10)->where('is_published', true)->avg('rating'), isCurrentPaginationPageOnly: true); +}); + +it('can count rows', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'count', $posts->count()); +}); + +it('can count rows on this pagination page', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'count', $posts->take(10)->count(), isCurrentPaginationPageOnly: true); +}); + +it('can count subset of rows', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('is_published', 'published_count', $posts->where('is_published', true)->count()); +}); + +it('can count subset of rows on this pagination page', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('is_published', 'published_count', $posts->take(10)->where('is_published', true)->count(), isCurrentPaginationPageOnly: true); +}); + +it('can get the range of values in a column', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'range', [$posts->min('rating'), $posts->max('rating')]); +}); + +it('can get the range of values in a column on this pagination page', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'range', [$posts->take(10)->min('rating'), $posts->take(10)->max('rating')], isCurrentPaginationPageOnly: true); +}); + +it('can get the range of a subset of values in a column', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'published_range', [$posts->where('is_published', true)->min('rating'), $posts->where('is_published', true)->max('rating')]); +}); + +it('can get the range of a subset of values in a column on this pagination page', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'published_range', [$posts->take(10)->where('is_published', true)->min('rating'), $posts->take(10)->where('is_published', true)->max('rating')], isCurrentPaginationPageOnly: true); +}); + +it('can get the range of values from a relationship in a column', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('author.name', 'range', [$posts->min('author.name'), $posts->max('author.name')]); +}); + +it('can get the range of values from a relationship in a column on this pagination page', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('author.name', 'range', [$posts->take(10)->min('author.name'), $posts->take(10)->max('author.name')], isCurrentPaginationPageOnly: true); +}); + +it('can sum values in a column', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'sum', $posts->sum('rating')); +}); + +it('can sum values in a column on this pagination page', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'sum', $posts->take(10)->sum('rating'), isCurrentPaginationPageOnly: true); +}); + +it('can sum subset of values in a column', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'published_sum', $posts->where('is_published', true)->sum('rating')); +}); + +it('can sum subset of values in a column on this pagination page', function () { + $posts = Post::factory()->count(20)->create(); + + livewire(PostsTable::class) + ->assertCanSeeTableRecords($posts->take(10)) + ->assertTableColumnSummarySet('rating', 'published_sum', $posts->take(10)->where('is_published', true)->sum('rating'), isCurrentPaginationPageOnly: true); +});