-
-
Notifications
You must be signed in to change notification settings - Fork 98
[feat] Add Select2Widget for choice fields #254 #642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| "use strict"; | ||
|
|
||
| django.jQuery(function ($) { | ||
| function initSelect2($element) { | ||
| $element.not('select[name*="__prefix__"]').each(function () { | ||
| var $el = $(this); | ||
| if (!$el.hasClass("select2-hidden-accessible")) { | ||
| $el.select2(); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| initSelect2($("select.ow-select2")); | ||
|
|
||
| $(document).on("formset:added", function (event, $row) { | ||
| initSelect2($row.find("select.ow-select2")); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| from django.conf import settings | ||
| from django.contrib.admin.widgets import SELECT2_TRANSLATIONS | ||
| from django.forms import Media, Select | ||
| from django.utils.translation import get_language | ||
|
|
||
|
|
||
| class Select2Widget(Select): | ||
| """Select2 autocomplete widget for Django ChoiceFields.""" | ||
|
|
||
| @property | ||
| def media(self): | ||
| extra = "" if getattr(settings, "DEBUG", False) else ".min" | ||
| i18n_name = SELECT2_TRANSLATIONS.get(get_language()) | ||
| i18n_file = ( | ||
| ("admin/js/vendor/select2/i18n/{0}.js".format(i18n_name),) | ||
| if i18n_name | ||
| else () | ||
| ) | ||
| return Media( | ||
| js=( | ||
| "admin/js/vendor/jquery/jquery{0}.js".format(extra), | ||
| "admin/js/vendor/select2/select2.full{0}.js".format(extra), | ||
| ) | ||
| + i18n_file | ||
| + ("admin/js/jquery.init.js", "openwisp-utils/js/select2.js"), | ||
| css={ | ||
| "screen": ("admin/css/vendor/select2/select2{0}.css".format(extra),), | ||
| }, | ||
| ) | ||
|
|
||
| def __init__(self, attrs=None, choices=()): | ||
| attrs = attrs or {} | ||
| attrs["class"] = "ow-select2 {0}".format(attrs.get("class", "")).strip() | ||
| super().__init__(attrs, choices) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| from channels.testing import ChannelsLiveServerTestCase | ||
| from django.test import TestCase, tag | ||
| from django.urls import reverse | ||
| from openwisp_utils.widgets import Select2Widget | ||
| from selenium.webdriver.common.by import By | ||
|
|
||
| from ..models import Shelf | ||
| from .utils import SeleniumTestMixin | ||
|
|
||
|
|
||
| class TestWidgets(TestCase): | ||
| def test_select2_widget_attrs(self): | ||
| widget = Select2Widget() | ||
| html = widget.render("name", "value") | ||
| self.assertIn('class="ow-select2"', html) | ||
|
|
||
| # test overriding works and class is preserved | ||
| widget = Select2Widget(attrs={"class": "my-class"}) | ||
| html = widget.render("name", "value") | ||
| self.assertIn("ow-select2 my-class", html) | ||
|
|
||
| def test_select2_widget_media(self): | ||
| widget = Select2Widget() | ||
| media = str(widget.media) | ||
| self.assertIn("admin/css/vendor/select2/select2", media) | ||
| self.assertIn("admin/js/vendor/jquery/jquery", media) | ||
| self.assertIn("admin/js/vendor/select2/select2.full", media) | ||
| self.assertIn("openwisp-utils/js/select2.js", media) | ||
|
|
||
|
|
||
| @tag("selenium_tests") | ||
| class TestSelect2AdminMixinSelenium(SeleniumTestMixin, ChannelsLiveServerTestCase): | ||
| def setUp(self): | ||
| super().setUp() | ||
| self.login() | ||
|
|
||
| def test_select2_widget_renders_on_shelf_add_form(self): | ||
| url = reverse("admin:test_project_shelf_add") | ||
| self.open(url) | ||
| self.wait_for_presence(By.CSS_SELECTOR, "select#id_books_type.ow-select2") | ||
|
|
||
|
Comment on lines
+37
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add-page Selenium test should also assert Select2 initialization, not just CSS class. This test currently proves widget markup but not JS enhancement on add view. Please also assert the Select2 container is present on this path. Suggested test tightening def test_select2_widget_renders_on_shelf_add_form(self):
url = reverse("admin:test_project_shelf_add")
self.open(url)
self.wait_for_presence(By.CSS_SELECTOR, "select#id_books_type.ow-select2")
+ self.wait_for_presence(By.CSS_SELECTOR, ".select2-container")Based on learnings: Features: Add tests for new features and ensure coverage does not decrease significantly; prefer Selenium browser tests for UI-impacting features. 🤖 Prompt for AI Agents |
||
| def test_select2_widget_renders_on_shelf_change_form(self): | ||
| shelf = Shelf.objects.create(name="Test Shelf", books_type="HORROR") | ||
| url = reverse("admin:test_project_shelf_change", args=[shelf.pk]) | ||
| self.open(url) | ||
| self.wait_for_presence(By.CSS_SELECTOR, "select#id_books_type.ow-select2") | ||
| self.wait_for_presence(By.CSS_SELECTOR, ".select2-container") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Avoid mutating caller-provided
attrsin place.Copy
attrsfirst to prevent side effects when the same dict instance is reused.Suggested refactor
📝 Committable suggestion
🤖 Prompt for AI Agents