[IMP] account,web: SearchPanel: move carets to the left

In this commit, hierarchy carets in the search panel have been moved to
the left side of the labels. To achieve a consistent and correct
alignment, some DOM elements have been added/moved and the related SCSS
sheets have been adapted.

Carets on filter sections of the search panel have also been removed.

Task 2258508
This commit is contained in:
Julien Mougenot
2020-05-26 13:09:00 +00:00
parent 41e409cff8
commit 22167aac57
5 changed files with 88 additions and 146 deletions
@@ -1,5 +1,5 @@
.o_search_panel.account_root {
flex: 0 0 48px;
flex: 0 0 50px;
padding: 6px;
scrollbar-width: thin;
.o_search_panel_section_header {
@@ -8,9 +8,14 @@
.list-group-item span.o_search_panel_label_title {
display: contents;
}
.o_search_panel_category_value header {
margin-left: 0;
padding-left: 0;
.o_search_panel_category_value {
header {
margin-left: 0;
padding-left: 0;
}
.o_search_panel_category_value .o_toggle_fold_container {
width: 0.3rem;
}
}
&::-webkit-scrollbar {
width: 4px;
+3 -17
View File
@@ -67,10 +67,9 @@ const SearchPanel = Widget.extend({
className: 'o_search_panel',
events: {
'click .o_search_panel_category_value header': '_onCategoryValueClicked',
'click .o_search_panel_category_value .o_toggle_fold': '_onToggleFoldCategory',
'click .o_search_panel_filter_group .o_toggle_fold': '_onToggleFoldFilterGroup',
'change .o_search_panel_filter_value > div > input': '_onFilterValueChanged',
'change .o_search_panel_filter_group > div > input': '_onFilterGroupChanged',
'click .o_search_panel_category_value .o_toggle_fold_container': '_onToggleFoldCategory',
'change .o_search_panel_filter_value > header > div > input': '_onFilterValueChanged',
'change .o_search_panel_filter_group > header > div > input': '_onFilterGroupChanged',
},
/**
@@ -783,19 +782,6 @@ const SearchPanel = Widget.extend({
category.values[valueId].folded = !category.values[valueId].folded;
this._render();
},
/**
* @private
* @param {MouseEvent} ev
*/
_onToggleFoldFilterGroup(ev) {
ev.preventDefault();
ev.stopPropagation();
const item = ev.currentTarget.closest('.o_search_panel_filter_group');
const filter = this.filters[item.dataset.filterId];
const groupId = item.dataset.groupId;
filter.groups[groupId].folded = !filter.groups[groupId].folded;
this._render();
},
});
return SearchPanel;
+37 -26
View File
@@ -27,48 +27,51 @@ $o-searchpanel-filter-default-color: #D59244;
.o_search_panel_category .o_search_panel_section_icon {
color: $o-brand-odoo;
}
.o_search_panel_filter .o_search_panel_section_icon {
color: $o-searchpanel-filter-default-color;
}
.o_toggle_fold_container {
text-align: center;
width: 1.5rem;
}
.o_search_panel_label {
align-items: center;
cursor: pointer;
display: flex;
justify-content: space-between;
user-select: none;
// Without counters: the caret is set at the right border
&:not(.o_with_counters) {
width: 100%;
}
.o_toggle_fold {
width: 2rem;
}
}
.o_search_panel_section_header {
padding: $o-searchpanel-p-small 0;
cursor: default;
}
.list-group {
padding-bottom: $o-searchpanel-p-tiny;
}
.list-group-item {
padding: 0 0 $o-searchpanel-p-small 0;
padding: 0;
.list-group-item {
padding: 0 0 0 $custom-control-gutter;
margin-bottom: $o-searchpanel-p-tiny*0.5;
&:first-child {
margin-top: $o-searchpanel-p-tiny*0.5;
}
}
span.o_search_panel_label_title {
color: $headings-color;
@include o-text-overflow;
}
header.active {
background-color: $o-searchpanel-active-bg;
}
}
.o_search_panel_category_value {
cursor: pointer;
@@ -76,9 +79,7 @@ $o-searchpanel-filter-default-color: #D59244;
align-items: center;
display: flex;
justify-content: space-between;
margin-top: -4px;
margin-bottom: -4px;
padding: 4px 6px;
padding: 4px 6px 4px 0px;
}
.o_search_panel_label.o_with_counters {
overflow: hidden;
@@ -86,7 +87,6 @@ $o-searchpanel-filter-default-color: #D59244;
.o_search_panel_category_value {
position: relative;
padding-left: $o-searchpanel-p;
padding-bottom: $o-searchpanel-p-tiny;
margin-bottom: 0;
&:before, &:after {
@@ -94,23 +94,34 @@ $o-searchpanel-filter-default-color: #D59244;
@include size(1px, 100%);
background: $gray-500;
content: '';
margin-left: 4px;
}
&:after {
top: 10px;
top: 12px;
@include size(8px, 1px);
}
&:last-child {
&:before {
height: 11px;
}
&:after {
top: 11px;
}
&:last-child:before {
height: 12px;
}
}
}
.o_search_panel_filter_value,
.o_search_panel_filter_group {
padding-right: 6px;
cursor: pointer;
padding-bottom: $o-searchpanel-p-small;
.o_search_panel_label,
.o_search_panel_label_title {
padding-right: 6px;
}
}
.o_search_panel_filter_group {
header {
display: flex;
}
.o_search_panel_label.o_with_counters {
justify-content: flex-start;
+32 -26
View File
@@ -225,7 +225,8 @@
<ul class="list-group d-block o_search_panel_field" t-att-data-field-name="category.fieldName">
<li class="o_search_panel_category_value border-0 list-group-item" t-att-data-category-id="category.id">
<header t-att-class="'list-group-item-action' + (!category.activeValueId ? ' active' : '')">
<label class="o_search_panel_label mb0 d-block">
<label class="o_search_panel_label mb0">
<div class="o_toggle_fold_container"/>
<span class="o_search_panel_label_title"><b>All</b></span>
</label>
</header>
@@ -245,16 +246,18 @@
<t t-set="value" t-value="category.values[valueId]"/>
<li class="o_search_panel_category_value border-0 list-group-item" t-att-data-id="value.id" t-att-data-category-id="category.id">
<header t-att-class="'list-group-item-action' + (value.id === category.activeValueId ? ' active' : '')">
<label t-att-for="value.display_name" t-attf-class="o_search_panel_label mb0{{ category.disableCounters ? '' : ' o_with_counters' }}">
<label t-attf-class="o_search_panel_label mb0{{ category.disableCounters ? '' : ' o_with_counters' }}">
<div class="o_toggle_fold_container">
<i t-if="value.childrenIds.length" t-attf-class="o_toggle_fold fa fa-caret-{{ value.folded ? 'right' : 'down' }}"/>
</div>
<span class="o_search_panel_label_title"><t t-esc="value.display_name"/></span>
<i t-if="value.childrenIds.length" t-attf-class="o_toggle_fold fa fa-fw fa-caret-{{ value.folded ? 'right' : 'down' }}"/>
</label>
<span t-if="!category.disableCounters and value.__count > 0"
class="text-muted ml-2 small"
t-esc="value.__count"
/>
</header>
<ul t-if="!value.folded" class="list-group d-block">
<ul t-if="!value.folded and value.childrenIds.length" class="list-group d-block">
<t t-call="SearchPanel.CategoryValues">
<t t-set="values" t-value="value.childrenIds"/>
</t>
@@ -286,17 +289,18 @@
<li t-foreach="filter.sortedGroupIds" t-as="groupId" t-att-data-group-id="groupId" t-att-data-filter-id="filter.id"
class="o_search_panel_filter_group list-group-item border-0">
<t t-set="group" t-value="groups[groupId]"/>
<div class="custom-control custom-checkbox">
<t t-set="inputId" t-value="_.uniqueId('input_')"/>
<input type="checkbox" t-att-id="inputId" t-att-checked="group.state === 'checked' ? 'checked' : undefined" class="custom-control-input"/>
<label t-att-for="inputId" t-attf-class="o_search_panel_label custom-control-label{{ group.disableCounters ? '' : ' o_with_counters' }}" t-att-title="group.tooltip">
<span class="o_search_panel_label_title">
<span t-if="group.hex_color" t-attf-style="color: {{ group.hex_color }};">●</span>
<t t-esc="group.name"/>
</span>
<i t-attf-class="o_toggle_fold fa fa-fw fa-caret-{{ group.folded ? 'right' : 'down' }}"/>
</label>
</div>
<t t-set="inputId" t-value="_.uniqueId('input_')"/>
<header class="o_search_panel_group_header">
<div class="custom-control custom-checkbox">
<input type="checkbox" t-att-id="inputId" t-att-checked="group.state === 'checked' ? 'checked' : undefined" class="custom-control-input"/>
<label t-att-for="inputId" t-attf-class="o_search_panel_label custom-control-label{{ group.disableCounters ? '' : ' o_with_counters' }}" t-att-title="group.tooltip">
<span class="o_search_panel_label_title">
<span t-if="group.hex_color" t-attf-style="color: {{ group.hex_color }};">●</span>
<t t-esc="group.name"/>
</span>
</label>
</div>
</header>
<ul t-if="!group.folded" class="list-group d-block">
<t t-call="SearchPanel.FilterValues">
<t t-set="sortedValueIds" t-value="group.sortedValueIds"/>
@@ -316,17 +320,19 @@
<li t-foreach="sortedValueIds" t-as="valueId" t-att-data-value-id="valueId" t-att-data-filter-id="filter.id"
class="o_search_panel_filter_value list-group-item border-0">
<t t-set="value" t-value="values[valueId]"/>
<div class="custom-control custom-checkbox">
<t t-set="inputId" t-value="_.uniqueId('input_')"/>
<input type="checkbox" t-att-id="inputId" t-att-checked="value.checked ? 'checked' : undefined" class="custom-control-input"/>
<label t-att-for="inputId" class="o_search_panel_label custom-control-label" t-att-title="group &amp;&amp; group.tooltip">
<span class="o_search_panel_label_title" t-esc="value.display_name"/>
<span t-if="!filter.disableCounters and value.__count > 0"
class="text-muted ml-2 small"
t-esc="value.__count"
/>
</label>
</div>
<t t-set="inputId" t-value="_.uniqueId('input_')"/>
<header class="o_search_panel_group_header">
<div class="custom-control custom-checkbox w-100">
<input type="checkbox" t-att-id="inputId" t-att-checked="value.checked ? 'checked' : undefined" class="custom-control-input"/>
<label t-att-for="inputId" class="o_search_panel_label custom-control-label" t-att-title="group &amp;&amp; group.tooltip">
<span class="o_search_panel_label_title" t-esc="value.display_name"/>
<span t-if="!filter.disableCounters and value.__count > 0"
class="text-muted ml-2 small"
t-esc="value.__count"
/>
</label>
</div>
</header>
</li>
</t>
@@ -1746,7 +1746,7 @@ QUnit.module('Views', {
assert.containsOnce(kanban, '.o_search_panel_filter_group:first .o_search_panel_filter_value');
assert.containsN(kanban, '.o_search_panel_filter_group:nth(1) .o_search_panel_filter_value', 2);
assert.containsNone(kanban, '.o_search_panel_filter_value input:checked');
assert.strictEqual(kanban.$('.o_search_panel_filter_group > div > label').text().replace(/\s/g, ''),
assert.strictEqual(kanban.$('.o_search_panel_filter_group > header > div > label').text().replace(/\s/g, ''),
'goldsilver');
assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
'asustek2agrolait1camptocamp');
@@ -1756,7 +1756,7 @@ QUnit.module('Views', {
await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:first input'));
assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked');
var firstGroupCheckbox = kanban.$('.o_search_panel_filter_group:first > div > input').get(0);
var firstGroupCheckbox = kanban.$('.o_search_panel_filter_group:first > header > div > input').get(0);
assert.strictEqual(firstGroupCheckbox.checked, true,
"first group checkbox should be checked");
assert.strictEqual(kanban.$('.o_search_panel_filter_value').text().replace(/\s/g, ''),
@@ -1767,7 +1767,7 @@ QUnit.module('Views', {
await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input'));
assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 2);
var secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0);
var secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > header > div > input').get(0);
assert.strictEqual(secondGroupCheckbox.checked, false,
"second group checkbox should not be checked");
assert.strictEqual(secondGroupCheckbox.indeterminate, true,
@@ -1780,7 +1780,7 @@ QUnit.module('Views', {
await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(2) input'));
assert.containsN(kanban, '.o_search_panel_filter_value input:checked', 3);
secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0);
secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > header > div > input').get(0);
assert.strictEqual(secondGroupCheckbox.checked, true,
"second group checkbox should be checked");
assert.strictEqual(secondGroupCheckbox.indeterminate, false,
@@ -1790,10 +1790,10 @@ QUnit.module('Views', {
assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 0);
// uncheck second group
await testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) > div > input'));
await testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) > header > div > input'));
assert.containsOnce(kanban, '.o_search_panel_filter_value input:checked');
secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0);
secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > header > div > input').get(0);
assert.strictEqual(secondGroupCheckbox.checked, false,
"second group checkbox should not be checked");
assert.strictEqual(secondGroupCheckbox.indeterminate, false,
@@ -1875,72 +1875,6 @@ QUnit.module('Views', {
kanban.destroy();
});
QUnit.test('(un)fold filter group', async function (assert) {
assert.expect(13);
this.data.company.records.push({id: 11, name: 'camptocamp', category_id: 7});
var kanban = await createView({
View: KanbanView,
model: 'partner',
data: this.data,
services: this.services,
arch: `
<kanban>
<templates>
<t t-name="kanban-box">
<div>
<field name="foo"/>
</div>
</t>
</templates>
</kanban>`,
archs: {
'partner,false,search': `
<search>
<searchpanel>
<field select="multi" name="company_id" groupby="category_id"/>
</searchpanel>
</search>`,
},
});
// groups are opened by default
assert.containsN(kanban, '.o_search_panel_filter_group', 2);
assert.containsN(kanban, '.o_search_panel_filter_value', 3);
// check 'agrolait'
await testUtils.dom.click(kanban.$('.o_search_panel_filter_value:nth(1) input'));
var secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0);
assert.strictEqual(secondGroupCheckbox.indeterminate, true);
assert.hasAttrValue(kanban.$('.o_search_panel_filter_value:nth(1) input'), 'checked', 'checked');
// fold second group
await testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) .o_toggle_fold'));
assert.containsN(kanban, '.o_search_panel_filter_group', 2);
assert.containsOnce(kanban, '.o_search_panel_filter_value');
secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0);
assert.strictEqual(secondGroupCheckbox.indeterminate, true);
// fold first group
await testUtils.dom.click(kanban.$('.o_search_panel_filter_group:first .o_toggle_fold'));
assert.containsN(kanban, '.o_search_panel_filter_group', 2);
assert.containsNone(kanban, '.o_search_panel_filter_value');
// unfold second group
await testUtils.dom.click(kanban.$('.o_search_panel_filter_group:nth(1) .o_toggle_fold'));
assert.containsN(kanban, '.o_search_panel_filter_group', 2);
assert.containsN(kanban, '.o_search_panel_filter_value', 2);
secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0);
assert.strictEqual(secondGroupCheckbox.indeterminate, true);
assert.hasAttrValue(kanban.$('.o_search_panel_filter_value:first input'), 'checked', 'checked');
kanban.destroy();
});
QUnit.test('filter with domain depending on category', async function (assert) {
assert.expect(22);
@@ -2155,7 +2089,7 @@ QUnit.module('Views', {
},
});
var secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > div > input').get(0);
var secondGroupCheckbox = kanban.$('.o_search_panel_filter_group:nth(1) > header > div > input').get(0);
assert.strictEqual(secondGroupCheckbox.indeterminate, true);
kanban.destroy();