general: add possibily to layout placeholder cells in flex grid (#62072)
This commit is contained in:
parent
9dba7d1245
commit
5b19cf1113
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.21 on 2022-02-22 14:21
|
||||
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('data', '0054_link_cell_bypass_validity_check'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='page',
|
||||
name='placeholder_options',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict),
|
||||
),
|
||||
]
|
|
@ -194,6 +194,7 @@ class Page(models.Model):
|
|||
exclude_from_navigation = models.BooleanField(_('Exclude from navigation'), default=True)
|
||||
redirect_url = models.CharField(_('Redirect URL'), max_length=200, blank=True)
|
||||
extra_variables = JSONField(blank=True, default=dict)
|
||||
placeholder_options = JSONField(blank=True, default=dict)
|
||||
|
||||
public = models.BooleanField(_('Public'), default=True)
|
||||
groups = models.ManyToManyField(Group, verbose_name=_('Groups'), blank=True)
|
||||
|
@ -879,6 +880,10 @@ class CellBase(models.Model, metaclass=CellMeta):
|
|||
]
|
||||
)
|
||||
|
||||
@property
|
||||
def cleaned_extra_css_class(self):
|
||||
return ' '.join([x for x in self.extra_css_class.split() if not x.startswith('size--')])
|
||||
|
||||
@property
|
||||
def asset_css_classes(self):
|
||||
from combo.apps.assets.models import Asset
|
||||
|
@ -1154,7 +1159,49 @@ class CellBase(models.Model, metaclass=CellMeta):
|
|||
(k, v['label']) for k, v in extra_templates.items()
|
||||
]
|
||||
widgets = {'template_name': forms.Select(choices=template_names)}
|
||||
return model_forms.modelform_factory(self.__class__, fields=fields, widgets=widgets)
|
||||
|
||||
page = self.page
|
||||
cell = self
|
||||
|
||||
class OptionsForm(model_forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if page.placeholder_options.get(cell.placeholder, {}).get('fx_grid_layout'):
|
||||
# add a size field that takes/stores its value in the extra_css_class
|
||||
# char field.
|
||||
self.fields['fx_size'] = forms.ChoiceField(
|
||||
label=_('Size'),
|
||||
choices=[
|
||||
('', ''),
|
||||
('size--1-1', '1/1'),
|
||||
('size--t1-2', '1/2'),
|
||||
('size--t1-3', '1/3'),
|
||||
],
|
||||
required=False,
|
||||
)
|
||||
# move extra_css_class field to be last
|
||||
field_order = list(self.fields.keys())
|
||||
field_order.remove('extra_css_class')
|
||||
field_order.append('extra_css_class')
|
||||
self.order_fields(field_order)
|
||||
extra_css_class = self.initial['extra_css_class'].split()
|
||||
for css_class, _dummy in self.fields['fx_size'].choices:
|
||||
if css_class in extra_css_class:
|
||||
extra_css_class.remove(css_class)
|
||||
self.initial['extra_css_class'] = ' '.join(extra_css_class)
|
||||
self.initial['fx_size'] = css_class
|
||||
break
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.cleaned_data.get('fx_size'):
|
||||
self.instance.extra_css_class = (
|
||||
'%s %s'
|
||||
% (self.instance.extra_css_class or '', self.cleaned_data.get('fx_size') or '')
|
||||
).strip()
|
||||
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
return model_forms.modelform_factory(self.__class__, form=OptionsForm, fields=fields, widgets=widgets)
|
||||
|
||||
def get_extra_manager_context(self):
|
||||
return {}
|
||||
|
|
|
@ -360,3 +360,16 @@ class SiteSettingsForm(forms.ModelForm):
|
|||
self.fields['initial_login_page'].widget.template_name = 'combo/widgets/select_with_other_option.html'
|
||||
self.fields['initial_login_page'].widget.attrs['class'] = 'page-selector'
|
||||
self.fields['welcome_page'].queryset = self.fields['welcome_page'].queryset.filter(public=True)
|
||||
|
||||
|
||||
class PlaceholderOptionsForm(forms.Form):
|
||||
fx_grid_layout = forms.ChoiceField(
|
||||
label=_('Grid Layout'),
|
||||
required=False,
|
||||
choices=[
|
||||
('', _('Manual')),
|
||||
('fx-grid', _('1 column')),
|
||||
('fx-grid--t2', _('2 columns')),
|
||||
('fx-grid--t3', _('3 columns')),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -30,9 +30,16 @@ div.page-options span.subslug {
|
|||
}
|
||||
|
||||
div.placeholder {
|
||||
position: relative;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.placeholder-options-link {
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
right: 1em;
|
||||
}
|
||||
|
||||
div#pages-list div.page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -156,6 +156,7 @@
|
|||
{% for placeholder in placeholders %}
|
||||
<div class="placeholder" data-placeholder-key="{{ placeholder.key }}">
|
||||
<h2>{{ placeholder.name }}</h2>
|
||||
<a class="placeholder-options-link" data-popup href="{% url 'combo-manage-placeholder-options' page_pk=object.id placeholder=placeholder.key %}">{% trans "Options" %}</a>
|
||||
<div class="cell-list">
|
||||
{% for cell in placeholder.cells %}
|
||||
<div id="cell-{{cell.get_reference}}" class="cell {{cell.class_name}}" data-cell-reference="{{ cell.get_reference }}">
|
||||
|
@ -165,7 +166,7 @@
|
|||
<span class="cell-template-label"
|
||||
>{% if cell.template_name %}({{cell.get_template_label}}){% endif %}</span>
|
||||
<span class="cell-slug">{% if cell.slug %}[{{cell.slug}}]{% endif %}</span>
|
||||
<span class="extra-css-class">{% if cell.extra_css_class %}[{{ cell.extra_css_class }}]{% endif %}</span>
|
||||
<span class="extra-css-class">{% if cell.cleaned_extra_css_class %}[{{ cell.cleaned_extra_css_class }}]{% endif %}</span>
|
||||
<span class="additional-label"><i>{{cell.get_additional_label|default_if_none:""}}</i></span>
|
||||
{% if cell.get_invalid_reason %}
|
||||
<span class="invalid">{{ cell.get_invalid_reason }}{% if cell.class_name != 'link-list-cell' %} -
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
{% extends "combo/manager_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans "Options" %}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<div class="buttons">
|
||||
<button class="submit-button">{% trans "Submit" %}</button>
|
||||
<a class="cancel" href="{% url 'combo-manager-page-view' pk=page.pk %}">{% trans 'Cancel' %}</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -137,6 +137,11 @@ urlpatterns = [
|
|||
name='combo-manager-link-list-order',
|
||||
),
|
||||
url(r'^pages/(?P<page_pk>\d+)/order$', views.cell_order, name='combo-manager-cell-order'),
|
||||
url(
|
||||
r'^pages/(?P<page_pk>\d+)/placeholder/(?P<placeholder>[\w_-]+)/options$',
|
||||
views.placeholder_options,
|
||||
name='combo-manage-placeholder-options',
|
||||
),
|
||||
url(r'^pages/order$', views.page_order, name='combo-manager-page-order'),
|
||||
url(
|
||||
r'^pages/(?P<page_path>[\w/_-]+)/$',
|
||||
|
|
|
@ -77,6 +77,7 @@ from .forms import (
|
|||
PageRestrictedAddForm,
|
||||
PageSelectTemplateForm,
|
||||
PageVisibilityForm,
|
||||
PlaceholderOptionsForm,
|
||||
SiteExportForm,
|
||||
SiteImportForm,
|
||||
SiteSettingsForm,
|
||||
|
@ -738,7 +739,7 @@ class PageEditCellView(ManagedPageMixin, UpdateView):
|
|||
|
||||
response['visibility_css_class'] = self.object.get_manager_visibility_css_class()
|
||||
response['visibility_content'] = self.object.get_manager_visibility_content()
|
||||
response['extra_css_class'] = self.object.extra_css_class
|
||||
response['extra_css_class'] = self.object.cleaned_extra_css_class
|
||||
response['slug'] = self.object.slug
|
||||
response['template_label'] = self.object.get_template_label()
|
||||
response['additional_label'] = self.object.get_additional_label()
|
||||
|
@ -1110,3 +1111,30 @@ class SiteSettingsView(UpdateView):
|
|||
|
||||
|
||||
site_settings = SiteSettingsView.as_view()
|
||||
|
||||
|
||||
class PlaceholderOptionsView(ManagedPageMixin, FormView):
|
||||
form_class = PlaceholderOptionsForm
|
||||
template_name = 'combo/placeholder_options.html'
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(Page, pk=self.kwargs['page_pk'])
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['page'] = self.get_object()
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
return self.page.placeholder_options.get(self.kwargs['placeholder'])
|
||||
|
||||
def form_valid(self, form):
|
||||
self.page.placeholder_options[self.kwargs['placeholder']] = form.cleaned_data
|
||||
self.page.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('combo-manager-page-view', kwargs={'pk': self.page.id})
|
||||
|
||||
|
||||
placeholder_options = PlaceholderOptionsView.as_view()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% load i18n %}
|
||||
{% if render %}
|
||||
{% if placeholder_options.fx_grid_layout %}<div class="combo-placeholder {{ placeholder_options.fx_grid_layout }}">{% endif %}
|
||||
{% if render_skeleton %}
|
||||
{{ skeleton }}
|
||||
{% endif %}
|
||||
|
@ -15,4 +16,5 @@
|
|||
{% endwith %}
|
||||
><div>{% render_cell cell %}</div></div>
|
||||
{% endfor %}
|
||||
{% if placeholder_options.fx_grid_layout %}</div>{% endif %}
|
||||
{% endif %}
|
||||
|
|
|
@ -67,6 +67,8 @@ def skeleton_text(context, placeholder_name, content=''):
|
|||
@register.inclusion_tag('combo/placeholder.html', takes_context=True)
|
||||
def placeholder(context, placeholder_name, **options):
|
||||
placeholder = Placeholder(key=placeholder_name, cell=context.get('cell'), **options)
|
||||
if 'page' in context:
|
||||
context['placeholder_options'] = context['page'].placeholder_options.get(placeholder_name) or {}
|
||||
# make sure render_skeleton is available in context
|
||||
context['render_skeleton'] = context.get('render_skeleton')
|
||||
if context.get('placeholder_search_mode'):
|
||||
|
|
|
@ -701,7 +701,7 @@ def test_edit_page_num_queries(settings, app, admin_user):
|
|||
app.get('/manage/pages/%s/' % page.pk) # load once to populate caches
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
app.get('/manage/pages/%s/' % page.pk)
|
||||
assert len(ctx.captured_queries) == 41
|
||||
assert len(ctx.captured_queries) == 44
|
||||
|
||||
|
||||
def test_delete_page(app, admin_user):
|
||||
|
@ -2456,7 +2456,7 @@ def test_page_versionning(app, admin_user):
|
|||
resp = resp.click('restore', index=6)
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
resp = resp.form.submit().follow()
|
||||
assert len(ctx.captured_queries) == 152
|
||||
assert len(ctx.captured_queries) == 155
|
||||
|
||||
resp2 = resp.click('See online')
|
||||
assert resp2.text.index('Foobar1') < resp2.text.index('Foobar2') < resp2.text.index('Foobar3')
|
||||
|
@ -2858,3 +2858,35 @@ def test_site_settings(app, admin_user):
|
|||
site_settings.refresh_from_db()
|
||||
assert site_settings.welcome_page == public_page
|
||||
assert site_settings.initial_login_page == private_page
|
||||
|
||||
|
||||
def test_manager_placeholder_grid(app, admin_user):
|
||||
page = Page.objects.create(title='Page', slug='page')
|
||||
cell = TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=1)
|
||||
app = login(app)
|
||||
resp = app.get('/manage/pages/%s/' % page.id)
|
||||
assert 'fx_size' not in resp.forms[0].fields
|
||||
resp = app.get('/manage/pages/%s/' % page.id)
|
||||
resp = resp.click('Options', href=r'placeholder.*options')
|
||||
resp.form['fx_grid_layout'].select(text='2 columns')
|
||||
resp = resp.form.submit().follow()
|
||||
|
||||
page.refresh_from_db()
|
||||
assert page.placeholder_options['content']['fx_grid_layout'] == 'fx-grid--t2'
|
||||
resp = resp.click('Options', href=r'placeholder.*options')
|
||||
assert resp.form['fx_grid_layout'].value == 'fx-grid--t2'
|
||||
|
||||
resp = app.get('/manage/pages/%s/' % page.id)
|
||||
assert 'fx_size' not in resp.forms[0].fields
|
||||
resp.forms[0]['cdata_textcell-%s-fx_size' % cell.id].select('size--t1-2')
|
||||
manager_submit_cell(resp.forms[0])
|
||||
cell.refresh_from_db()
|
||||
assert cell.extra_css_class == 'size--t1-2'
|
||||
|
||||
assert resp.forms[0]['cdata_textcell-%s-fx_size' % cell.id].value == 'size--t1-2'
|
||||
assert not resp.forms[0]['cdata_textcell-%s-extra_css_class' % cell.id].value
|
||||
resp.forms[0]['cdata_textcell-%s-extra_css_class' % cell.id] = 'plop'
|
||||
resp_sub = manager_submit_cell(resp.forms[0])
|
||||
assert resp_sub.json['extra_css_class'] == 'plop'
|
||||
cell.refresh_from_db()
|
||||
assert set(cell.extra_css_class.split()) == {'plop', 'size--t1-2'}
|
||||
|
|
|
@ -1225,3 +1225,22 @@ def test_cell_asset_css_classes(settings, app, admin_user):
|
|||
assert cell.asset_css_classes == ''
|
||||
resp = app.get('/', status=200)
|
||||
assert 'class="cell text-cell textcell foo"' in re.sub(r' +', ' ', resp.text)
|
||||
|
||||
|
||||
def test_placeholder_grid(app):
|
||||
Page.objects.all().delete()
|
||||
page = Page(title='Home', slug='index', template_name='standard')
|
||||
page.save()
|
||||
|
||||
TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=1)
|
||||
|
||||
resp = app.get('/', status=200)
|
||||
# no placeholder div if there's no grid layout
|
||||
assert resp.pyquery('#content > .text-cell')
|
||||
|
||||
page.placeholder_options = {'content': {'fx_grid_layout': 'fx-grid'}}
|
||||
page.save()
|
||||
|
||||
resp = app.get('/', status=200)
|
||||
# placeholder div in between
|
||||
assert resp.pyquery('#content > .fx-grid > .text-cell')
|
||||
|
|
Loading…
Reference in New Issue