Skip to content

Commit

Permalink
Merge branch 'feature/changelist-text' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
abidibo committed Dec 4, 2020
2 parents 20cbbb5 + 1b1d1a6 commit 5d4a411
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 21 deletions.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Login with user `demo` and password `demo`
- [Signals](#signals)
- [Js Utilities](#js-utilities)
- [List Filters](#list-filters)
- [Changelist Includes](#changelist-includes)
- [Form Tabs](#form-tabs)
- [Form Includes](#form-includes)
- [Collapsable stacked inlines entries](#collapsable-stackedinline)
Expand Down Expand Up @@ -435,6 +436,35 @@ class MyModelAdmin(admin.ModelAdmin):
)
```

## <a name="changelist-includes"></a>Changelist Includes

Baton lets you include templates directly inside the change list page, in any position you desire. It's as simple as specifying the template path and the position of the template:

```python
@admin.register(News)
class NewsAdmin(admin.ModelAdmin):
#...
baton_cl_includes = [
('news/admin_include_top.html', 'top', ),
('news/admin_include_below.html', 'below', )
]
```

In this case, Baton will place the content of the `admin_include_top.html` template at the top of the changelist section (above the search field), and the content of the `admin_include_below.html` below the changelist form.

![Baton changelist includes](docs/images/baton-cl-includes.png)

You can specify the following positions:

|Position|Description|
|:--------|:-----------|
|`top`| the template is placed inside the changelist form, at the top|
|`bottom`| the template is placed inside the changelist form, at the bottom|
|`above`| the template is placed above the changelist form|
|`below`| the template is placed below the changelist form|

And, of course, you can access the all the changelist view context variables inside your template.

## <a name="form-tabs"></a>Form tabs

How much I loved django-suit form tabs? Too much. So, this was a feature I couldn't live without.
Expand Down
4 changes: 2 additions & 2 deletions baton/static/baton/app/dist/baton.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion baton/static/baton/app/dist/baton.min.js.map

Large diffs are not rendered by default.

37 changes: 19 additions & 18 deletions baton/static/baton/app/src/core/ChangeList.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ let ChangeList = {
if (this._filtersDiv.length) {
this.activate()
}
this.initTemplates()
},
activate: function () {
if ($('.changelist-form-container').length) { // django >= 3.1
Expand All @@ -42,17 +43,16 @@ let ChangeList = {
let title = titleEl.html()
titleEl.remove()
let content = $('#changelist-filter-modal')[0].outerHTML
// remove from dom
$('#changelist-filter-modal').remove()
this.modal = new Modal({
title,
content,
size: 'md',
hideFooter: true,
})
// this.modal = this.createModal()
_filtersToggler
.click(() => {
// this.modal.modal('toggle')
this.modal.toggle()
})
} else {
Expand All @@ -70,22 +70,23 @@ let ChangeList = {
$('#changelist-form .results').css('padding-top', '78px')
}
},
createModal: function () {
let modal = $('<div />', { 'class': 'modal' })
let modalContent = `
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">${this.t.get('close')}</button>
</div>
</div>
</div>
`
modal.html(modalContent)
modal.find('.modal-content').prepend($('#changelist-filter-modal'))
let title = modal.find('#changelist-filter-modal > h2')
modal.find('.modal-content').prepend(title.addClass('modal-header').css('margin-bottom', 0))
return modal
initTemplates: function () {
const positionMap = {
above: 'before',
below: 'after',
top: 'prepend',
bottom: 'append',
}

$('template').each(function (index, template) {
let position = positionMap[$(template).attr('data-position')]
if (position !== undefined) {
let el = $('#changelist-form')
el[position]($(template).html())
} else {
console.error('Baton: wrong changelist include position detected')
}
})
}
}

Expand Down
12 changes: 12 additions & 0 deletions baton/static/baton/app/src/styles/_changelist.scss
Original file line number Diff line number Diff line change
Expand Up @@ -378,4 +378,16 @@
}
}
}

.baton-cl-include-top {
padding-right: 50px;

@include media-breakpoint-up(sm) {
padding-right: 100px;
}
}

.baton-cl-include-below {
order: 2;
}
}
11 changes: 11 additions & 0 deletions baton/templates/admin/change_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% extends 'admin/change_list.html' %}
{% block content %}{{ block.super }}

{% for template, position in cl.model_admin.baton_cl_includes %}
<template data-position="{{ position }}">
<div class="baton-cl-include baton-cl-include-{{ position }}">
{% include template %}
</div>
</template>
{% endfor %}
{% endblock %}
32 changes: 32 additions & 0 deletions docs/changelist_includes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Form includes
=========

.. image:: images/baton-cl-includes.png

Baton lets you include templates directly inside the change list page, in any position you desire. It's as simple as specifying the template path and the position of the template::

@admin.register(News)
class NewsAdmin(admin.ModelAdmin):
#...
baton_cl_includes = [
('news/admin_include_top.html', 'top', ),
('news/admin_include_below.html', 'below', )
]

In this case, Baton will place the content of the ``admin_include_top.html`` template at the top of the changelist section (above the search field), and the content of the ``admin_include_below.html`` below the changelist form.

You can specify the following positions:

+----------------------------------------+--------------------------------------------------------------------+
| Position | Description |
+========================================+====================================================================+
| top | the template is placed inside the changelist form, at the top |
+----------------------------------------+--------------------------------------------------------------------+
| bottom | the template is placed inside the changelist form, at the bottom |
+----------------------------------------+--------------------------------------------------------------------+
| above | the template is placed above the changelist form |
+----------------------------------------+--------------------------------------------------------------------+
| below | the template is placed below the changelist form |
+----------------------------------------+--------------------------------------------------------------------+

And, of course, you can access the all the changelist view context variables inside your template.
Binary file added docs/images/baton-cl-includes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Getting started
signals
js_utilities
list_filters
changelist_includes
form_tabs
form_includes
collapsable_stackedinline
Expand Down
57 changes: 57 additions & 0 deletions testapp/app/app/tests/test_e2e_cl_includes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import os
import time

from django.test import TestCase
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager

from .utils import element_has_css_class

os.environ['WDM_LOG_LEVEL'] = '0'


class TestBatonClIncludes(TestCase):
def setUp(self):
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--disable-dev-shm-usage')
self.driver = webdriver.Chrome(
ChromeDriverManager().install(),
options=chrome_options,
)
self.driver.set_window_size(1920, 1080)
self.driver.implicitly_wait(10)
self.login()

def tearDown(self):
self.driver.quit()

def login(self):
self.driver.get('http://localhost:8000/admin/news/news/')
username_field = self.driver.find_element_by_id("id_username")
password_field = self.driver.find_element_by_id("id_password")
button = self.driver.find_element_by_css_selector('input[type=submit]')

username_field.send_keys('admin')
time.sleep(1)
password_field.send_keys('admin')
time.sleep(1)
button.click()

def test_includes(self):
# Wait until baton is ready
wait = WebDriverWait(self.driver, 10)
wait.until(element_has_css_class((By.TAG_NAME, 'body'), "baton-ready"))
time.sleep(2)

# tabs number
include = self.driver.find_element_by_css_selector(
'.baton-cl-include-top')
self.assertEqual(include.is_displayed(), True)
parent = include.find_element_by_xpath('..')
self.assertEqual(parent.get_attribute('id'), 'changelist-form')
Binary file modified testapp/app/db.sqlite3
Binary file not shown.
4 changes: 4 additions & 0 deletions testapp/app/news/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,7 @@ class NewsAdmin(admin.ModelAdmin):
('news/admin_content_include.html', 'content', 'above', ),
('news/admin_title_include.html', 'title', 'right', ),
]

baton_cl_includes = [
('news/admin_cl_top_include.html', 'top', ),
]
3 changes: 3 additions & 0 deletions testapp/app/news/templates/news/admin_cl_top_include.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<p>
In ut quam vitae odio lacinia tincidunt. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. Quisque id mi. Vestibulum dapibus nunc ac augue. Praesent ac massa at ligula laoreet iaculis. Nullam quis ante. Nam at tortor in tellus interdum sagittis. Praesent egestas neque eu enim. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Aliquam eu nunc.
</p>

0 comments on commit 5d4a411

Please sign in to comment.