From 032c4bc17d02803e92d56b0ef97e270cc4ea5a32 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 5 Nov 2025 16:12:52 +0000 Subject: [PATCH] Implement Widget Positioning Methods: [after()](cci:1://file:///d:/Ninja_Work/Git_work/CRUD/src/app/Library/Widget.php:108:4-141:5) and [before()](cci:1://file:///d:/Ninja_Work/Git_work/CRUD/src/app/Library/Widget.php:143:4-176:5) --- src/app/Library/Widget.php | 64 ++++++++++- tests/Unit/CrudPanel/WidgetTest.php | 163 ++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 tests/Unit/CrudPanel/WidgetTest.php diff --git a/src/app/Library/Widget.php b/src/app/Library/Widget.php index 3bd64f162b..95f81e4226 100644 --- a/src/app/Library/Widget.php +++ b/src/app/Library/Widget.php @@ -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; } /** diff --git a/tests/Unit/CrudPanel/WidgetTest.php b/tests/Unit/CrudPanel/WidgetTest.php new file mode 100644 index 0000000000..4e7db8a84d --- /dev/null +++ b/tests/Unit/CrudPanel/WidgetTest.php @@ -0,0 +1,163 @@ +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']); + } +}