mirror of
https://github.com/sissbruecker/linkding.git
synced 2025-08-08 11:18:28 +02:00
Add option to collapse side panel (#975)
This commit is contained in:
48
bookmarks/e2e/e2e_test_collapse_side_panel.py
Normal file
48
bookmarks/e2e/e2e_test_collapse_side_panel.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
from django.urls import reverse
|
||||||
|
from playwright.sync_api import sync_playwright, expect
|
||||||
|
|
||||||
|
from bookmarks.e2e.helpers import LinkdingE2ETestCase
|
||||||
|
|
||||||
|
|
||||||
|
class CollapseSidePanelE2ETestCase(LinkdingE2ETestCase):
|
||||||
|
|
||||||
|
def setUp(self) -> None:
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
def assertSidePanelIsVisible(self):
|
||||||
|
expect(self.page.locator(".bookmarks-page .side-panel")).to_be_visible()
|
||||||
|
expect(
|
||||||
|
self.page.locator(".bookmarks-page [ld-tag-modal-trigger]")
|
||||||
|
).not_to_be_visible()
|
||||||
|
|
||||||
|
def assertSidePanelIsHidden(self):
|
||||||
|
expect(self.page.locator(".bookmarks-page .side-panel")).not_to_be_visible()
|
||||||
|
expect(
|
||||||
|
self.page.locator(".bookmarks-page [ld-tag-modal-trigger]")
|
||||||
|
).to_be_visible()
|
||||||
|
|
||||||
|
def test_side_panel_should_be_visible_by_default(self):
|
||||||
|
with sync_playwright() as p:
|
||||||
|
self.open(reverse("bookmarks:index"), p)
|
||||||
|
self.assertSidePanelIsVisible()
|
||||||
|
|
||||||
|
self.page.goto(self.live_server_url + reverse("bookmarks:archived"))
|
||||||
|
self.assertSidePanelIsVisible()
|
||||||
|
|
||||||
|
self.page.goto(self.live_server_url + reverse("bookmarks:shared"))
|
||||||
|
self.assertSidePanelIsVisible()
|
||||||
|
|
||||||
|
def test_side_panel_should_be_hidden_when_collapsed(self):
|
||||||
|
user = self.get_or_create_test_user()
|
||||||
|
user.profile.collapse_side_panel = True
|
||||||
|
user.profile.save()
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
self.open(reverse("bookmarks:index"), p)
|
||||||
|
self.assertSidePanelIsHidden()
|
||||||
|
|
||||||
|
self.page.goto(self.live_server_url + reverse("bookmarks:archived"))
|
||||||
|
self.assertSidePanelIsHidden()
|
||||||
|
|
||||||
|
self.page.goto(self.live_server_url + reverse("bookmarks:shared"))
|
||||||
|
self.assertSidePanelIsHidden()
|
18
bookmarks/migrations/0043_userprofile_collapse_side_panel.py
Normal file
18
bookmarks/migrations/0043_userprofile_collapse_side_panel.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.5 on 2025-02-02 09:35
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookmarks", "0042_userprofile_custom_css_hash"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="userprofile",
|
||||||
|
name="collapse_side_panel",
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
@@ -440,6 +440,7 @@ class UserProfile(models.Model):
|
|||||||
null=False, default=30, validators=[MinValueValidator(10)]
|
null=False, default=30, validators=[MinValueValidator(10)]
|
||||||
)
|
)
|
||||||
sticky_pagination = models.BooleanField(default=False, null=False)
|
sticky_pagination = models.BooleanField(default=False, null=False)
|
||||||
|
collapse_side_panel = models.BooleanField(default=False, null=False)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.custom_css:
|
if self.custom_css:
|
||||||
@@ -479,6 +480,7 @@ class UserProfileForm(forms.ModelForm):
|
|||||||
"auto_tagging_rules",
|
"auto_tagging_rules",
|
||||||
"items_per_page",
|
"items_per_page",
|
||||||
"sticky_pagination",
|
"sticky_pagination",
|
||||||
|
"collapse_side_panel",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@@ -36,7 +36,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
& .sections section {
|
& .sections section {
|
||||||
margin-top: var(--unit-4);
|
margin-top: var(--unit-4);
|
||||||
}
|
}
|
||||||
|
@@ -10,10 +10,40 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Bookmark page grid */
|
/* Bookmark page grid */
|
||||||
.bookmarks-page.grid {
|
.bookmarks-page {
|
||||||
|
&.grid {
|
||||||
grid-gap: var(--unit-9);
|
grid-gap: var(--unit-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ld-tag-modal-trigger] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 840px) {
|
||||||
|
section.side-panel {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ld-tag-modal-trigger] {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.collapse-side-panel {
|
||||||
|
section.main {
|
||||||
|
grid-column: span var(--grid-columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
section.side-panel {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ld-tag-modal-trigger] {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Bookmark area header controls */
|
/* Bookmark area header controls */
|
||||||
.bookmarks-page .search-container {
|
.bookmarks-page .search-container {
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
|
@@ -4,16 +4,17 @@
|
|||||||
{% load bookmarks %}
|
{% load bookmarks %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div ld-bulk-edit class="bookmarks-page grid columns-md-1">
|
<div ld-bulk-edit
|
||||||
|
class="bookmarks-page grid columns-md-1 {% if bookmark_list.collapse_side_panel %}collapse-side-panel{% endif %}">
|
||||||
|
|
||||||
{# Bookmark list #}
|
{# Bookmark list #}
|
||||||
<section class="content-area col-2">
|
<section class="main content-area col-2">
|
||||||
<div class="content-area-header mb-0">
|
<div class="content-area-header mb-0">
|
||||||
<h2>Archived bookmarks</h2>
|
<h2>Archived bookmarks</h2>
|
||||||
<div class="header-controls">
|
<div class="header-controls">
|
||||||
{% bookmark_search bookmark_list.search mode='archived' %}
|
{% bookmark_search bookmark_list.search mode='archived' %}
|
||||||
{% include 'bookmarks/bulk_edit/toggle.html' %}
|
{% include 'bookmarks/bulk_edit/toggle.html' %}
|
||||||
<button ld-tag-modal-trigger class="btn ml-2 show-md">Tags
|
<button ld-tag-modal-trigger class="btn ml-2">Tags
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{# Tag cloud #}
|
{# Tag cloud #}
|
||||||
<section class="content-area col-1 hide-md">
|
<section class="side-panel content-area col-1">
|
||||||
<div class="content-area-header">
|
<div class="content-area-header">
|
||||||
<h2>Tags</h2>
|
<h2>Tags</h2>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -4,16 +4,17 @@
|
|||||||
{% load bookmarks %}
|
{% load bookmarks %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div ld-bulk-edit class="bookmarks-page grid columns-md-1">
|
<div ld-bulk-edit
|
||||||
|
class="bookmarks-page grid columns-md-1 {% if bookmark_list.collapse_side_panel %}collapse-side-panel{% endif %}">
|
||||||
|
|
||||||
{# Bookmark list #}
|
{# Bookmark list #}
|
||||||
<section class="content-area col-2">
|
<section class="main content-area col-2">
|
||||||
<div class="content-area-header mb-0">
|
<div class="content-area-header mb-0">
|
||||||
<h2>Bookmarks</h2>
|
<h2>Bookmarks</h2>
|
||||||
<div class="header-controls">
|
<div class="header-controls">
|
||||||
{% bookmark_search bookmark_list.search %}
|
{% bookmark_search bookmark_list.search %}
|
||||||
{% include 'bookmarks/bulk_edit/toggle.html' %}
|
{% include 'bookmarks/bulk_edit/toggle.html' %}
|
||||||
<button ld-tag-modal-trigger class="btn ml-2 show-md">Tags</button>
|
<button ld-tag-modal-trigger class="btn ml-2">Tags</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{# Tag cloud #}
|
{# Tag cloud #}
|
||||||
<section class="content-area col-1 hide-md">
|
<section class="side-panel content-area col-1">
|
||||||
<div class="content-area-header">
|
<div class="content-area-header">
|
||||||
<h2>Tags</h2>
|
<h2>Tags</h2>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -4,15 +4,16 @@
|
|||||||
{% load bookmarks %}
|
{% load bookmarks %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="bookmarks-page grid columns-md-1">
|
<div
|
||||||
|
class="bookmarks-page grid columns-md-1 {% if bookmark_list.collapse_side_panel %}collapse-side-panel{% endif %}">
|
||||||
|
|
||||||
{# Bookmark list #}
|
{# Bookmark list #}
|
||||||
<section class="content-area col-2">
|
<section class="main content-area col-2">
|
||||||
<div class="content-area-header">
|
<div class="content-area-header">
|
||||||
<h2>Shared bookmarks</h2>
|
<h2>Shared bookmarks</h2>
|
||||||
<div class="header-controls">
|
<div class="header-controls">
|
||||||
{% bookmark_search bookmark_list.search mode='shared' %}
|
{% bookmark_search bookmark_list.search mode='shared' %}
|
||||||
<button ld-tag-modal-trigger class="btn ml-2 show-md">Tags
|
<button ld-tag-modal-trigger class="btn ml-2">Tags
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{# Filters #}
|
{# Filters #}
|
||||||
<section class="content-area col-1 hide-md">
|
<section class="side-panel content-area col-1">
|
||||||
<div class="content-area-header">
|
<div class="content-area-header">
|
||||||
<h2>User</h2>
|
<h2>User</h2>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -124,6 +124,16 @@
|
|||||||
visible without having to scroll to the end of the page first.
|
visible without having to scroll to the end of the page first.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ form.collapse_side_panel.id_for_label }}" class="form-checkbox">
|
||||||
|
{{ form.collapse_side_panel }}
|
||||||
|
<i class="form-icon"></i> Collapse tags
|
||||||
|
</label>
|
||||||
|
<div class="form-input-hint">
|
||||||
|
When enabled, the tags side panel will be collapsed by default to give more space to the bookmark list.
|
||||||
|
Instead, the tags can be shown in a modal dialog by clicking the tags button in the bookmark list header.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="{{ form.tag_search.id_for_label }}" class="form-label">Tag search</label>
|
<label for="{{ form.tag_search.id_for_label }}" class="form-label">Tag search</label>
|
||||||
{{ form.tag_search|add_class:"form-select width-25 width-sm-100" }}
|
{{ form.tag_search|add_class:"form-select width-25 width-sm-100" }}
|
||||||
|
@@ -47,6 +47,7 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
|
|||||||
"auto_tagging_rules": "",
|
"auto_tagging_rules": "",
|
||||||
"items_per_page": "30",
|
"items_per_page": "30",
|
||||||
"sticky_pagination": False,
|
"sticky_pagination": False,
|
||||||
|
"collapse_side_panel": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
return {**form_data, **overrides}
|
return {**form_data, **overrides}
|
||||||
@@ -117,6 +118,7 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
|
|||||||
"auto_tagging_rules": "example.com tag",
|
"auto_tagging_rules": "example.com tag",
|
||||||
"items_per_page": "10",
|
"items_per_page": "10",
|
||||||
"sticky_pagination": True,
|
"sticky_pagination": True,
|
||||||
|
"collapse_side_panel": True,
|
||||||
}
|
}
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("bookmarks:settings.update"), form_data, follow=True
|
reverse("bookmarks:settings.update"), form_data, follow=True
|
||||||
@@ -194,6 +196,9 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.user.profile.sticky_pagination, form_data["sticky_pagination"]
|
self.user.profile.sticky_pagination, form_data["sticky_pagination"]
|
||||||
)
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.user.profile.collapse_side_panel, form_data["collapse_side_panel"]
|
||||||
|
)
|
||||||
|
|
||||||
self.assertSuccessMessage(html, "Profile updated")
|
self.assertSuccessMessage(html, "Profile updated")
|
||||||
|
|
||||||
|
@@ -208,6 +208,7 @@ class BookmarkListContext:
|
|||||||
self.show_favicons = user_profile.enable_favicons
|
self.show_favicons = user_profile.enable_favicons
|
||||||
self.show_preview_images = user_profile.enable_preview_images
|
self.show_preview_images = user_profile.enable_preview_images
|
||||||
self.show_notes = user_profile.permanent_notes
|
self.show_notes = user_profile.permanent_notes
|
||||||
|
self.collapse_side_panel = user_profile.collapse_side_panel
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_return_url(search: BookmarkSearch, base_url: str, page: int = None):
|
def generate_return_url(search: BookmarkSearch, base_url: str, page: int = None):
|
||||||
|
Reference in New Issue
Block a user