[REF] web: introduces search bar toggler component
Part-of: odoo/odoo#116641
This commit is contained in:
committed by
Pierre Paridans
parent
eef262abf4
commit
475aa6ca49
@@ -68,6 +68,7 @@
|
||||
</t>
|
||||
</nav>
|
||||
</t>
|
||||
<t t-slot="control-panel-navigation-additional"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,18 +9,8 @@ import { DomainSelectorDialog } from "@web/core/domain_selector_dialog/domain_se
|
||||
import { fuzzyTest } from "@web/core/utils/search";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { SearchBarMenu } from "../search_bar_menu/search_bar_menu";
|
||||
import { useDebounced } from "@web/core/utils/timing";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { SIZES } from "@web/core/ui/ui_service";
|
||||
|
||||
import {
|
||||
Component,
|
||||
onMounted,
|
||||
onWillUnmount,
|
||||
useExternalListener,
|
||||
useRef,
|
||||
useState,
|
||||
} from "@odoo/owl";
|
||||
import { Component, useExternalListener, useRef, useState } from "@odoo/owl";
|
||||
const parsers = registry.category("parsers");
|
||||
|
||||
const CHAR_FIELDS = ["char", "html", "many2many", "many2one", "one2many", "text", "properties"];
|
||||
@@ -41,7 +31,6 @@ export class SearchBar extends Component {
|
||||
expanded: [],
|
||||
focusedIndex: 0,
|
||||
query: "",
|
||||
showSearchBar: this.ui.size > SIZES.SM,
|
||||
});
|
||||
|
||||
// derived state
|
||||
@@ -64,10 +53,6 @@ export class SearchBar extends Component {
|
||||
|
||||
useExternalListener(window, "click", this.onWindowClick);
|
||||
useExternalListener(window, "keydown", this.onWindowKeydown);
|
||||
|
||||
this.onResize = useDebounced(this.onResize, 200);
|
||||
onMounted(() => browser.addEventListener("resize", this.onResize));
|
||||
onWillUnmount(() => browser.removeEventListener("resize", this.onResize));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,10 +428,6 @@ export class SearchBar extends Component {
|
||||
this.inputRef.el.focus();
|
||||
}
|
||||
|
||||
onResize() {
|
||||
this.state.showSearchBar = this.ui.size > SIZES.SM;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {KeyboardEvent} ev
|
||||
*/
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
</t>
|
||||
|
||||
<t t-name="web.SearchBar" owl="1">
|
||||
<div t-if="state.showSearchBar" class="o_cp_searchview d-flex input-group" role="search" t-ref="root">
|
||||
<div class="o_cp_searchview d-flex input-group" role="search" t-ref="root">
|
||||
<div class="o_searchview form-control d-print-contents d-flex align-items-center py-1 border-end-0" role="search" aria-autocomplete="list">
|
||||
<i class="o_searchview_icon d-print-none oi oi-search me-2"
|
||||
role="img"
|
||||
@@ -97,11 +97,6 @@
|
||||
</div>
|
||||
<SearchBarMenu/>
|
||||
</div>
|
||||
<t t-portal="'.o_control_panel_navigation,.o_searchview'">
|
||||
<button t-attf-class="btn btn-secondary {{state.showSearchBar?'d-md-none active':''}}" t-on-click="onToggleSearchBar">
|
||||
<i class="fa fa-fw fa-search"/>
|
||||
</button>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Component, useEffect, useState } from "@odoo/owl";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { useDebounced } from "@web/core/utils/timing";
|
||||
|
||||
export class SearchBarToggler extends Component {}
|
||||
SearchBarToggler.template = "web.SearchBar.Toggler";
|
||||
SearchBarToggler.props = {
|
||||
isSmall: Boolean,
|
||||
showSearchBar: Boolean,
|
||||
toggleSearchBar: Function,
|
||||
};
|
||||
|
||||
export function useSearchBarToggler() {
|
||||
const ui = useService("ui");
|
||||
|
||||
let isToggled = false;
|
||||
const state = useState({
|
||||
isSmall: ui.isSmall,
|
||||
showSearchBar: false,
|
||||
});
|
||||
const updateState = () => {
|
||||
state.isSmall = ui.isSmall;
|
||||
state.showSearchBar = !ui.isSmall || isToggled;
|
||||
};
|
||||
updateState();
|
||||
|
||||
function toggleSearchBar() {
|
||||
isToggled = !isToggled;
|
||||
updateState();
|
||||
}
|
||||
|
||||
const onResize = useDebounced(updateState, 200);
|
||||
useEffect(
|
||||
() => {
|
||||
browser.addEventListener("resize", onResize);
|
||||
return () => browser.removeEventListener("resize", onResize);
|
||||
},
|
||||
() => []
|
||||
);
|
||||
|
||||
return {
|
||||
state,
|
||||
component: SearchBarToggler,
|
||||
get props() {
|
||||
return {
|
||||
isSmall: state.isSmall,
|
||||
showSearchBar: state.showSearchBar,
|
||||
toggleSearchBar,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="web.SearchBar.Toggler" owl="1">
|
||||
<button t-if="props.isSmall" t-attf-class="btn btn-secondary {{ props.showSearchBar ? 'active' : '' }}" t-on-click="props.toggleSearchBar">
|
||||
<i class="fa fa-fw fa-search"/>
|
||||
</button>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -13,6 +13,7 @@ import { CalendarFilterPanel } from "./filter_panel/calendar_filter_panel";
|
||||
import { CalendarMobileFilterPanel } from "./mobile_filter_panel/calendar_mobile_filter_panel";
|
||||
import { CalendarQuickCreate } from "./quick_create/calendar_quick_create";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
import { useSearchBarToggler } from "@web/search/search_bar/search_bar_toggler";
|
||||
import { ViewScaleSelector } from "@web/views/view_components/view_scale_selector";
|
||||
import { CogMenu } from "@web/search/cog_menu/cog_menu";
|
||||
|
||||
@@ -57,6 +58,8 @@ export class CalendarController extends Component {
|
||||
this.state = useState({
|
||||
showSideBar: !this.env.isSmall,
|
||||
});
|
||||
|
||||
this.searchBarToggler = useSearchBarToggler();
|
||||
}
|
||||
|
||||
get rendererProps() {
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
<t t-call="{{ props.buttonTemplate }}"/>
|
||||
</t>
|
||||
<t t-set-slot="layout-actions">
|
||||
<SearchBar/>
|
||||
<SearchBar t-if="searchBarToggler.state.showSearchBar"/>
|
||||
</t>
|
||||
<t t-set-slot="control-panel-navigation-additional">
|
||||
<t t-component="searchBarToggler.component" t-props="searchBarToggler.props"/>
|
||||
</t>
|
||||
<div class="o_calendar_container">
|
||||
<MobileFilterPanel t-if="env.isSmall" t-props="mobileFilterPanelProps" />
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useModel } from "@web/views/model";
|
||||
import { standardViewProps } from "@web/views/standard_view_props";
|
||||
import { useSetupView } from "@web/views/view_hook";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
import { useSearchBarToggler } from "@web/search/search_bar/search_bar_toggler";
|
||||
import { CogMenu } from "@web/search/cog_menu/cog_menu";
|
||||
|
||||
import { Component, useRef } from "@odoo/owl";
|
||||
@@ -21,6 +22,7 @@ export class GraphController extends Component {
|
||||
},
|
||||
getContext: () => this.getContext(),
|
||||
});
|
||||
this.searchBarToggler = useSearchBarToggler();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -57,7 +57,10 @@
|
||||
<CogMenu/>
|
||||
</t>
|
||||
<t t-set-slot="layout-actions">
|
||||
<SearchBar/>
|
||||
<SearchBar t-if="searchBarToggler.state.showSearchBar"/>
|
||||
</t>
|
||||
<t t-set-slot="control-panel-navigation-additional">
|
||||
<t t-component="searchBarToggler.component" t-props="searchBarToggler.props"/>
|
||||
</t>
|
||||
<t t-if="model.data">
|
||||
<t t-if="model.useSampleModel and props.info.noContentHelp" t-call="web.ActionHelper">
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useViewButtons } from "@web/views/view_button/view_button_hook";
|
||||
import { useSetupView } from "@web/views/view_hook";
|
||||
import { KanbanRenderer } from "./kanban_renderer";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
import { useSearchBarToggler } from "@web/search/search_bar/search_bar_toggler";
|
||||
import { CogMenu } from "@web/search/cog_menu/cog_menu";
|
||||
|
||||
import { Component, reactive, useRef } from "@odoo/owl";
|
||||
@@ -78,6 +79,7 @@ export class KanbanController extends Component {
|
||||
};
|
||||
}
|
||||
});
|
||||
this.searchBarToggler = useSearchBarToggler();
|
||||
}
|
||||
|
||||
get modelParams() {
|
||||
|
||||
@@ -33,7 +33,10 @@
|
||||
<CogMenu/>
|
||||
</t>
|
||||
<t t-set-slot="layout-actions">
|
||||
<SearchBar/>
|
||||
<SearchBar t-if="searchBarToggler.state.showSearchBar"/>
|
||||
</t>
|
||||
<t t-set-slot="control-panel-navigation-additional">
|
||||
<t t-component="searchBarToggler.component" t-props="searchBarToggler.props"/>
|
||||
</t>
|
||||
<t t-component="props.Renderer"
|
||||
list="model.root"
|
||||
|
||||
@@ -21,6 +21,7 @@ import { ExportDataDialog } from "@web/views/view_dialogs/export_data_dialog";
|
||||
import { useSetupView } from "@web/views/view_hook";
|
||||
import { ListConfirmationDialog } from "./list_confirmation_dialog";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
import { useSearchBarToggler } from "@web/search/search_bar/search_bar_toggler";
|
||||
import { Dropdown } from "@web/core/dropdown/dropdown";
|
||||
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
|
||||
import { CogMenu } from "@web/search/cog_menu/cog_menu";
|
||||
@@ -144,6 +145,7 @@ export class ListController extends Component {
|
||||
},
|
||||
() => [this.model.root.selection.length]
|
||||
);
|
||||
this.searchBarToggler = useSearchBarToggler();
|
||||
}
|
||||
|
||||
get modelParams() {
|
||||
@@ -594,7 +596,17 @@ export class ListController extends Component {
|
||||
}
|
||||
|
||||
ListController.template = `web.ListView`;
|
||||
ListController.components = { ActionMenus, Layout, ViewButton, MultiRecordViewButton, SearchBar, Dropdown, DropdownItem, CogMenu, ActionMenusItems };
|
||||
ListController.components = {
|
||||
ActionMenus,
|
||||
Layout,
|
||||
ViewButton,
|
||||
MultiRecordViewButton,
|
||||
SearchBar,
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
CogMenu,
|
||||
ActionMenusItems,
|
||||
};
|
||||
ListController.props = {
|
||||
...standardViewProps,
|
||||
allowSelectors: { type: Boolean, optional: true },
|
||||
|
||||
@@ -32,7 +32,10 @@
|
||||
</t>
|
||||
|
||||
<t t-set-slot="layout-actions">
|
||||
<SearchBar/>
|
||||
<SearchBar t-if="searchBarToggler.state.showSearchBar"/>
|
||||
</t>
|
||||
<t t-set-slot="control-panel-navigation-additional">
|
||||
<t t-component="searchBarToggler.component" t-props="searchBarToggler.props"/>
|
||||
</t>
|
||||
|
||||
<t t-set-slot="control-panel-additional-actions">
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useModel } from "@web/views/model";
|
||||
import { standardViewProps } from "@web/views/standard_view_props";
|
||||
import { useSetupView } from "@web/views/view_hook";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
import { useSearchBarToggler } from "@web/search/search_bar/search_bar_toggler";
|
||||
import { CogMenu } from "@web/search/cog_menu/cog_menu";
|
||||
|
||||
import { Component, useRef } from "@odoo/owl";
|
||||
@@ -21,6 +22,7 @@ export class PivotController extends Component {
|
||||
},
|
||||
getContext: () => this.getContext(),
|
||||
});
|
||||
this.searchBarToggler = useSearchBarToggler();
|
||||
}
|
||||
/**
|
||||
* @returns {Object}
|
||||
|
||||
@@ -25,7 +25,10 @@
|
||||
<CogMenu/>
|
||||
</t>
|
||||
<t t-set-slot="layout-actions">
|
||||
<SearchBar/>
|
||||
<SearchBar t-if="searchBarToggler.state.showSearchBar"/>
|
||||
</t>
|
||||
<t t-set-slot="control-panel-navigation-additional">
|
||||
<t t-component="searchBarToggler.component" t-props="searchBarToggler.props"/>
|
||||
</t>
|
||||
<t t-set="displayNoContent" t-value="
|
||||
props.info.noContentHelp !== false and (
|
||||
|
||||
@@ -67,7 +67,7 @@ QUnit.module("Mobile Views", ({ beforeEach }) => {
|
||||
await triggerEvents(fixture, ".o_data_row:nth-child(1)", ["touchstart", "touchend"]);
|
||||
assert.containsOnce(fixture, ".o_list_selection_box");
|
||||
assert.containsNone(fixture, ".o_list_selection_box .o_list_select_domain");
|
||||
assert.containsNone(fixture, ".o_control_panel .fa-search");
|
||||
assert.containsNone(fixture, ".o_control_panel .o_cp_searchview");
|
||||
assert.ok(
|
||||
fixture.querySelector(".o_list_selection_box").textContent.includes("1 selected")
|
||||
);
|
||||
|
||||
@@ -739,6 +739,7 @@ QUnit.module("ViewDialogs", (hooks) => {
|
||||
activateElement: () => {},
|
||||
deactivateElement: () => {},
|
||||
size: 4,
|
||||
isSmall: true,
|
||||
};
|
||||
const fakeUIService = {
|
||||
start(env) {
|
||||
|
||||
@@ -419,7 +419,7 @@ stepUtils.autoExpandMoreButtons('.o_form_saved'),
|
||||
content: _t('Save'),
|
||||
position: 'right',
|
||||
}, {
|
||||
trigger: '.o_form_button_save',
|
||||
trigger: '.o_form_status_indicator .o_form_button_save',
|
||||
content: _t("Save this product and the modifications you've made to it."),
|
||||
position: 'bottom',
|
||||
}, {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo
|
||||
import odoo.tests
|
||||
import unittest
|
||||
|
||||
class BaseTestUi(odoo.tests.HttpCase):
|
||||
|
||||
@@ -81,5 +79,4 @@ class TestUiMobile(BaseTestUi):
|
||||
touch_enabled = True
|
||||
|
||||
def test_01_main_flow_tour_mobile(self):
|
||||
raise unittest.SkipTest("Skip Milk")
|
||||
# self.main_flow_tour()
|
||||
self.main_flow_tour()
|
||||
|
||||
Reference in New Issue
Block a user