Skip to content

Commit

Permalink
Add Dropdown Widget
Browse files Browse the repository at this point in the history
Widget code added.
  • Loading branch information
mohsin committed Jan 1, 2017
1 parent 0900b31 commit b8c8a69
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 100 deletions.
17 changes: 16 additions & 1 deletion controllers/Users.php
Expand Up @@ -2,6 +2,8 @@

use BackendMenu;
use Backend\Classes\Controller;
use October\Test\Models\Role;
use October\Test\Widgets\Dropdown;

/**
* Users Back-end Controller
Expand All @@ -18,10 +20,23 @@ class Users extends Controller
public $listConfig = 'config_list.yaml';
public $relationConfig = 'config_relation.yaml';

protected $dropdownWidget;

public function __construct()
{
parent::__construct();

BackendMenu::setContext('October.Test', 'test', 'users');

$this->dropdownWidget = new Dropdown($this);
$this->dropdownWidget->alias = 'roles';
$this->dropdownWidget->setListItems(Role::lists('name', 'id'));
$this->dropdownWidget->setErrorMessage('Items list empty. First add items to the roles model.');
$this->dropdownWidget->bindToController();
}

public function listExtendQuery($query)
{
$query->withRole($this->dropdownWidget->getActiveIndex());
}
}
}
9 changes: 7 additions & 2 deletions controllers/users/_list_toolbar.htm
@@ -1,3 +1,8 @@
<div data-control="toolbar">
<a href="<?= Backend::url('october/test/users/create') ?>" class="btn btn-primary oc-icon-plus">New User</a>
</div>
<div class="toolbar-item toolbar-primary">
<a href="<?= Backend::url('october/test/users/create') ?>" class="btn btn-primary oc-icon-plus">New User</a>
</div>
<div class="toolbar-item toolbar-primary">
<?= $this->dropdownWidget->render() ?>
</div>
</div>
7 changes: 7 additions & 0 deletions models/User.php
Expand Up @@ -86,4 +86,11 @@ public function scopeApplyRoleFilter($query, $filtered)
});
}

public function scopeWithRole($query, $filtered)
{
return $query->whereHas('roles', function($q) use ($filtered) {
$q->where('id', $filtered);
});
}

}
103 changes: 6 additions & 97 deletions readme.md
@@ -1,100 +1,9 @@
# Test Plugin
# Dropdown Widget

This is a UI test plugin for OctoberCMS. Extract this archive to `/plugins/october/test` and click on **Playground** in the back-end area.
This is a mirror of [Playground for OctoberCMS](https://github.com/daftspunk/oc-test-plugin) to demonstrate the dropdown widget.

The following sections are explored, tested and demonstrated along with a list of the features used:
### Introduction
You can make filters using the filter option in OctoberCMS. But this does not include a dropdown filter. I needed this functionality and so made this widget to help achieve this. I am using this widget as part of the [Mobile plugin](http://octobercms.com/plugin/mohsin-mobile).

### Test 1: People

A Person "Has One" Phone (One to one relationship)

1. Relation Controller
1. Record Finder
1. Proxy Form fields
1. Date pickers
1. Context-based Form fields
1. List search policies `@todo`

### Test 2: Posts

A Post "Has Many" Comments (One to many relationship)

1. Relation Controller
1. Popup-based Form fields
1. Rich Editor
1. Dual Form Controller and List Controller
1. HTML in comments
1. Custom Delete workflow
1. Repeater fields in comments popup

### Test 3: Users

User "Belongs To Many" Roles (Many to many relationship)

1. Relation Controller (Standard, Pivot data, Pivot model)
1. Image Uploaders (Single, Multi, File, Image)
1. Number field
1. No click list column
1. Custom File model
1. Form field partial
1. Tag List in relation mode

### Test 4: Countries

A Country "Has Many" Posts "Through" a User (Has many through relationship)

1. Checkbox list
1. Default form field values
1. Field dependency and filtering
1. Repeater fields
1. Tabs empty with no fields

### Test 5: Reviews

1. Reviews "Morph To" Plugins and Themes as Product (Polymorphic relationships)
1. Meta "Morph To" Plugins and Themes as Product (Polymorphic relationships)
1. Plugins and Themes "Morph Many" Reviews
1. Plugins and Themes "Morph One" Meta
1. Plugins should not create when Meta validation fails.

### Test 6: Galleries

1. Galleries are "Morphed By Many" Posts
1. Posts "Morph To Many" Galleries

### Test 7: Trees

1. A Member uses a simple tree (parent-child) structure.
1. A Category uses a simple tree structure, with sorting.
1. A Channel uses a nested set tree structure.

### Test 8: Attributes

An Attribute is a single generic model with many relationship types.

1. Posts "Belong To" (Attribute) Status (`general.status`).
1. Countries "Belong To Many" (Attribute) Types (`general.types`).

## Tests that need attention

- An attach relation when required inside a tab does not make the tab active.

- Proxy fields throw a nasty error when the relation is non existant.

- Deleting multiple Comments from a Post (deferred) only deletes one comment.

- Constrained relations should not appear in RecordFinder (Phone:is_active) and Relation Controller lists (Comment:is_visible).

- Record Finder does not incorporate deferred bindings.

- HasOne relations acting as HasMany will break the list completely.

- Pivot model with required field doesn't show the asterisk on the form.

## Incorporating functional tests

- All relation controllers

- Test that input preset API works on fields

- Test that trigger API works on fields
### Demonstration
[![Video Demonstration](http://img.youtube.com/vi/qbrnvjOFvPI/0.jpg)](http://www.youtube.com/watch?v=qbrnvjOFvPI)
137 changes: 137 additions & 0 deletions widgets/Dropdown.php
@@ -0,0 +1,137 @@
<?php namespace October\Test\Widgets;

use Backend\Classes\WidgetBase;

class Dropdown extends WidgetBase
{
/**
* @var constant The default error for empty items list
*/
const DEFAULT_ERROR = 'Items list empty. First add items to the related model.';

/**
* @var string A unique alias to identify this widget.
*/
protected $defaultAlias = 'dropdown';

/**
* @var string Default error for empty items list
*/
protected $defaultError = self::DEFAULT_ERROR;

/**
* @var integer The first item of index that shows in button
*/
protected $index = 1;

/**
* @var array Cache List of items to show in dropdown
*/
protected $listItems = [];

public function __construct($controller, $listItems = [], $defaultError = self::DEFAULT_ERROR)
{
parent::__construct($controller);
$this -> listItems = $listItems;
$this->defaultError = $defaultError;
}

/**
* Renders the widget.
*/
public function render()
{
$this->prepareVars();
return $this->makePartial('dropdown');
}

/**
* Prepares the view data
*/
public function prepareVars()
{
$this->vars['index'] = $this->getActiveIndex();
$this->vars['items'] = $this->getListItems();
$this->vars['error_message'] = $this->defaultError;
}

public function onItemChange()
{
/*
* Save or reset dropdown index in session
*/
$this->setActiveIndex(post('index'));
$widgetId = '#' . $this -> getId();
$listId = '#' . $this->controller->listGetWidget()->getId();
$listRefreshData = $this->controller->listRefresh();

return [
$listId => $listRefreshData[$listId],
$widgetId => $this->makePartial('dropdown', ['index' => $this->getActiveIndex(), 'items' => $this->getListItems()])
];
}

/**
* Gets the list items array for this widget instance.
*/
public function getListItems()
{
return $this->listItems;
}

/**
* Sets the list items array for this widget instance.
*/
public function setListItems($listItems)
{
$this->listItems = $listItems;
}

/**
* Gets the error message for this widget instance.
*/
public function getErrorMessage()
{
return $this->defaultError;
}

/**
* Sets the error message for this widget instance.
*/
public function setErrorMessage($message)
{
$this->defaultError = $message;
}

/**
* Returns an active index for this widget instance.
*/
public function getActiveIndex()
{
return $this->index = $this->getSession('index', 1);
}

/**
* Sets an active index for this widget instance.
*/
public function setActiveIndex($index)
{
if ($index) {
$this->putSession('index', $index);
}
else {
$this->resetSession();
}

$this->index = $index;
}

/**
* Returns a value suitable for the field name property.
* @return string
*/
public function getName()
{
return $this->alias . '[index]';
}
}
40 changes: 40 additions & 0 deletions widgets/dropdown/partials/_dropdown.htm
@@ -0,0 +1,40 @@
<div id="<?= $this->getId() ?>" data-control="toolbar">
<?php if (count($items) == 0): ?>
<div class="callout fade in callout-success">
<div class="content">
<p><?php echo e(trans($this->getErrorMessage())) ?></p>
</div>
</div>
<?php else: ?>
<div class="dropdown dropdown-fixed">
<button
id="parentDropdown"
type="button"
class="btn btn-default oc-icon-mobile"
data-toggle="dropdown">
<span id="initialItem"><?php echo $items[$this->getActiveIndex()] ?><?php if (count($items) > 1): ?></span> <span class="caret"><?php endif ?>
</button>
<?php if (count($items) > 1): ?>
<ul id="dropdownList" class="dropdown-menu" role="menu">
<?php foreach ($items as $k => $item): ?>
<?php if ($k != $index): ?>
<li role="presentation">
<a
name="<?= $this->getName() ?>"
data-request="<?= $this->getEventHandler('onItemChange') ?>"
data-request-data="index: <?= $k ?>"
data-stripe-load-indicator
role="menuitem"
tabindex="-1"
href="#"
class="oc-icon-android">
<?= $item ?>
</a>
</li>
<?php endif ?>
<?php endforeach ?>
</ul>
<?php endif ?>
</div>
<?php endif ?>
</div>

0 comments on commit b8c8a69

Please sign in to comment.