Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 62 additions & 2 deletions src/app/Library/Widget.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,74 @@ public function forget($attribute)
return $this;
}

// TODO: add ability to push a widget right after another widget
/**
* Move this widget to appear right after another widget.
*
* @param string $destination The name of the destination widget.
* @return Widget
*/
public function after($destination)
{
$collection = $this->collection();
$widgets = $collection->all();

// Check if destination widget exists
if (! isset($widgets[$destination])) {
return $this;
}

// Remove current widget from collection
$currentWidget = $collection->pull($this->attributes['name']);

// Find the position of the destination widget
$keys = array_keys($widgets);
$position = array_search($destination, $keys);

// Insert after the destination
$before = array_slice($widgets, 0, $position + 1, true);
$after = array_slice($widgets, $position + 1, null, true);

$newWidgets = $before + [$this->attributes['name'] => $currentWidget] + $after;

// Update the collection
$collection->replace($newWidgets);

return $this;
}

// TODO: add ability to push a widget right before another widget
/**
* Move this widget to appear right before another widget.
*
* @param string $destination The name of the destination widget.
* @return Widget
*/
public function before($destination)
{
$collection = $this->collection();
$widgets = $collection->all();

// Check if destination widget exists
if (! isset($widgets[$destination])) {
return $this;
}

// Remove current widget from collection
$currentWidget = $collection->pull($this->attributes['name']);

// Find the position of the destination widget
$keys = array_keys($widgets);
$position = array_search($destination, $keys);

// Insert before the destination
$before = array_slice($widgets, 0, $position, true);
$after = array_slice($widgets, $position, null, true);

$newWidgets = $before + [$this->attributes['name'] => $currentWidget] + $after;

// Update the collection
$collection->replace($newWidgets);

return $this;
}

/**
Expand Down
163 changes: 163 additions & 0 deletions tests/Unit/CrudPanel/WidgetTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php

namespace Backpack\CRUD\Tests\Unit\CrudPanel;

use Backpack\CRUD\app\Library\Widget;
use Backpack\CRUD\Tests\BaseTestClass;

class WidgetTest extends BaseTestClass
{
protected function setUp(): void
{
parent::setUp();

// Clear the widgets collection before each test
Widget::collection()->forget(Widget::collection()->keys()->all());
}

public function testAfterMethodMovesWidgetAfterDestination()
{
// Create three widgets
Widget::add(['name' => 'widget_1', 'type' => 'card']);
Widget::add(['name' => 'widget_2', 'type' => 'card']);
Widget::add(['name' => 'widget_3', 'type' => 'card']);

// Move widget_3 after widget_1
Widget::add('widget_3')->after('widget_1');

$keys = Widget::collection()->keys()->toArray();

// Expected order: widget_1, widget_3, widget_2
$this->assertEquals(['widget_1', 'widget_3', 'widget_2'], $keys);
}

public function testBeforeMethodMovesWidgetBeforeDestination()
{
// Create three widgets
Widget::add(['name' => 'widget_1', 'type' => 'card']);
Widget::add(['name' => 'widget_2', 'type' => 'card']);
Widget::add(['name' => 'widget_3', 'type' => 'card']);

// Move widget_3 before widget_1
Widget::add('widget_3')->before('widget_1');

$keys = Widget::collection()->keys()->toArray();

// Expected order: widget_3, widget_1, widget_2
$this->assertEquals(['widget_3', 'widget_1', 'widget_2'], $keys);
}

public function testAfterMethodWithNonExistentDestinationDoesNothing()
{
// Create two widgets
Widget::add(['name' => 'widget_1', 'type' => 'card']);
Widget::add(['name' => 'widget_2', 'type' => 'card']);

// Try to move widget_2 after a non-existent widget
Widget::add('widget_2')->after('non_existent');

$keys = Widget::collection()->keys()->toArray();

// Order should remain unchanged
$this->assertEquals(['widget_1', 'widget_2'], $keys);
}

public function testBeforeMethodWithNonExistentDestinationDoesNothing()
{
// Create two widgets
Widget::add(['name' => 'widget_1', 'type' => 'card']);
Widget::add(['name' => 'widget_2', 'type' => 'card']);

// Try to move widget_2 before a non-existent widget
Widget::add('widget_2')->before('non_existent');

$keys = Widget::collection()->keys()->toArray();

// Order should remain unchanged
$this->assertEquals(['widget_1', 'widget_2'], $keys);
}

public function testAfterMethodWorksWithMultipleWidgets()
{
// Create five widgets
Widget::add(['name' => 'widget_1', 'type' => 'card']);
Widget::add(['name' => 'widget_2', 'type' => 'card']);
Widget::add(['name' => 'widget_3', 'type' => 'card']);
Widget::add(['name' => 'widget_4', 'type' => 'card']);
Widget::add(['name' => 'widget_5', 'type' => 'card']);

// Move widget_5 after widget_2
Widget::add('widget_5')->after('widget_2');

$keys = Widget::collection()->keys()->toArray();

// Expected order: widget_1, widget_2, widget_5, widget_3, widget_4
$this->assertEquals(['widget_1', 'widget_2', 'widget_5', 'widget_3', 'widget_4'], $keys);
}

public function testBeforeMethodWorksWithMultipleWidgets()
{
// Create five widgets
Widget::add(['name' => 'widget_1', 'type' => 'card']);
Widget::add(['name' => 'widget_2', 'type' => 'card']);
Widget::add(['name' => 'widget_3', 'type' => 'card']);
Widget::add(['name' => 'widget_4', 'type' => 'card']);
Widget::add(['name' => 'widget_5', 'type' => 'card']);

// Move widget_1 before widget_4
Widget::add('widget_1')->before('widget_4');

$keys = Widget::collection()->keys()->toArray();

// Expected order: widget_2, widget_3, widget_1, widget_4, widget_5
$this->assertEquals(['widget_2', 'widget_3', 'widget_1', 'widget_4', 'widget_5'], $keys);
}

public function testAfterMethodReturnsWidgetInstance()
{
Widget::add(['name' => 'widget_1', 'type' => 'card']);
$widget = Widget::add(['name' => 'widget_2', 'type' => 'card']);

$result = $widget->after('widget_1');

$this->assertInstanceOf(Widget::class, $result);
}

public function testBeforeMethodReturnsWidgetInstance()
{
Widget::add(['name' => 'widget_1', 'type' => 'card']);
$widget = Widget::add(['name' => 'widget_2', 'type' => 'card']);

$result = $widget->before('widget_1');

$this->assertInstanceOf(Widget::class, $result);
}

public function testAfterMethodCanBeChained()
{
Widget::add(['name' => 'widget_1', 'type' => 'card']);
Widget::add(['name' => 'widget_2', 'type' => 'card']);

Widget::add(['name' => 'widget_3', 'type' => 'card'])
->after('widget_1')
->type('div');

$widget = Widget::collection()->get('widget_3');

$this->assertEquals('div', $widget->attributes['type']);
}

public function testBeforeMethodCanBeChained()
{
Widget::add(['name' => 'widget_1', 'type' => 'card']);
Widget::add(['name' => 'widget_2', 'type' => 'card']);

Widget::add(['name' => 'widget_3', 'type' => 'card'])
->before('widget_2')
->type('div');

$widget = Widget::collection()->get('widget_3');

$this->assertEquals('div', $widget->attributes['type']);
}
}