Use activate instead of changeActivePanel/ChangeActiveTab

Remove shop-tabs controllers since we can listen on `"data-action":
"orderCycleSelected@window->tabs-and-panels#activateDefaultPanel"`

Test for cases:
* activate by clicking on tab
* activateDefaultPanel on orderCycleSelected event
* activateFromWindowLocationOrDefaultPanelTarget to activate tab based
  on achor in URL
This commit is contained in:
Dusan Orlovic
2023-11-14 07:40:07 +01:00
parent ba7ffc25fe
commit 120e299653
11 changed files with 170 additions and 218 deletions

View File

@@ -14,7 +14,7 @@
{{'hubs_delivery' | t}}
.row
.columns.small-12
%a.cta-hub{"ng-href" => "{{::enterprise.path}}#/shop", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}",
%a.cta-hub{"ng-href" => "{{::enterprise.path}}#/shop_panel", "ng-attr-target" => "{{ embedded_layout ? '_blank' : undefined}}",
"ng-class" => "{primary: enterprise.active, secondary: !enterprise.active}",
"ng-click" => "$close()",
"ofn-change-hub" => "enterprise"}

View File

@@ -12,7 +12,7 @@
.row
.columns.small-12
%a.cta-hub{"ng-repeat" => "hub in enterprise.hubs | filter:{id: '!'+enterprise.id} | orderBy:'-active'",
"ng-href" => "{{::hub.path}}#/shop", "ofn-empties-cart" => "hub",
"ng-href" => "{{::hub.path}}#/shop_panel", "ofn-empties-cart" => "hub",
"ng-class" => "::{primary: hub.active, secondary: !hub.active}",
"ng-click" => "$close()",
"ofn-change-hub" => "hub"}

View File

@@ -20,6 +20,6 @@ module SharedHelper
end
def current_shop_products_path
"#{main_app.enterprise_shop_path(current_distributor)}#/shop"
"#{main_app.enterprise_shop_path(current_distributor)}#/shop_panel"
end
end

View File

@@ -2,12 +2,12 @@
- if @enterprise
- enterprise_side_menu_items(@enterprise).each do |item|
- next if !item[:show] || (item[:name] == 'vouchers' && !feature?(:vouchers, spree_current_user, @enterprise))
%a.menu_item{ href: item[:href] || "##{item[:name]}_panel", id: item[:name], data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" }, class: item[:selected] }
%a.menu_item{ href: item[:href] || "##{item[:name]}_panel", data: { action: "tabs-and-panels#activate", "tabs-and-panels-target": "tab", test: "link_for_#{item[:name]}" }, class: item[:selected] }
%i{ class: item[:icon_class] }
%span= t(".enterprise.#{item[:name] }")
- else
- enterprise_group_side_menu_items.each do |item|
%a.menu_item{ href: "##{item[:name]}_panel", class: item[:selected], id: item[:name], data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" } }
%a.menu_item{ href: "##{item[:name]}_panel", class: item[:selected], data: { action: "tabs-and-panels#activate", "tabs-and-panels-target": "tab", test: "link_for_#{item[:name]}" } }
%i{ class: item[:icon_class] }
%span= t(".enterprise_group.#{item[:name] }")

View File

@@ -8,6 +8,6 @@
%p
= t('.require_login_link_html', login: ('<a data-action="click->login-modal#call">' + t('.login') + '</a>').html_safe)
%p
= t('.require_login_2_html', contact: link_to(t('.contact'), '#contact_panel', data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" }, id: "contact"), enterprise: current_distributor.name)
= t('.require_login_2_html', contact: link_to(t('.contact'), '#contact_panel', data: { action: "tabs-and-panels#activate" }), enterprise: current_distributor.name)
- else
= t('.require_customer_html', contact: link_to(t('.contact'), '#contact_panel', data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" }, id: "contact"), enterprise: current_distributor.name)
= t('.require_customer_html', contact: link_to(t('.contact'), '#contact_panel', data: { action: "tabs-and-panels#activate" }), enterprise: current_distributor.name)

View File

@@ -1,12 +1,12 @@
- if (@order&.distributor || current_distributor) == current_distributor
#shop-tabs{"data-controller": "tabs-and-panels shop-tabs", "data-tabs-and-panels-class-name-value": "selected"}
#shop-tabs{"data-controller": "tabs-and-panels", "data-action": "orderCycleSelected@window->tabs-and-panels#activateDefaultPanel", "data-tabs-and-panels-class-name-value": "selected"}
.tab-buttons
.flex.row
.columns.small-12.large-8
- shop_tabs.each do |tab|
.page
%a{ id: tab[:name], href: "##{tab[:name]}_panel", data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" }, class: ("selected" if tab[:default]) }=tab[:title]
%a{ href: "##{tab[:name]}_panel", data: { action: "tabs-and-panels#activate", "tabs-and-panels-target": "tab" }, class: ("selected" if tab[:default]) }=tab[:title]
.columns.large-4.show-for-large-up
= render partial: "shopping_shared/order_cycles"
- shop_tabs.each do |tab|

View File

@@ -6,7 +6,7 @@
= t(:order_back_to_cart)
- else
.columns.small-12.medium-6
= link_to "#{main_app.enterprise_shop_path(@order.distributor)}#/shop", class: "button expand" do
= link_to "#{main_app.enterprise_shop_path(@order.distributor)}#/shop_panel", class: "button expand" do
= t(:order_back_to_store)
.columns.small-12.medium-6
- if @order.distributor.website.present?

View File

@@ -1,22 +0,0 @@
import { Controller } from "stimulus";
export default class extends Controller {
connect() {
window.addEventListener("orderCycleSelected", this.orderCycleSelected);
}
disconnect() {
window.removeEventListener("orderCycleSelected", this.orderCycleSelected);
}
orderCycleSelected = (event) => {
window.dispatchEvent(
new CustomEvent("tabs-and-panels:click", {
detail: {
tab: "shop",
panel: "shop_panel",
},
})
);
};
}

View File

@@ -5,93 +5,46 @@ export default class extends Controller {
static values = { className: String };
connect() {
// hide all active panel
this.panelTargets.forEach((panel) => {
panel.style.display = "none";
});
// only display the default panel
this.defaultTarget.style.display = "block";
// Display panel specified in url anchor
const anchors = window.location.toString().split("#");
let anchor = anchors.length > 1 ? anchors.pop() : "";
if (anchor != "") {
// Conveniently AngularJs rewrite "example.com#panel" to "example.com#/panel" :(
// strip the starting / if any
if (anchor[0] == "/") {
anchor = anchor.slice(1);
}
// Add _panel to the anchor to match the panel id if needed
if (!anchor.includes("_panel")) {
anchor = `${anchor}_panel`;
}
this.updateActivePanel(anchor);
// tab
const tab_id = anchor.split("_panel").shift();
this.updateActiveTab(tab_id);
}
window.addEventListener("tabs-and-panels:click", (event) => {
this.simulateClick(event.detail.tab, event.detail.panel);
});
this._activateFromWindowLocationOrDefaultPanelTarget();
window.addEventListener("popstate", (event) => {
const newPanelId = event.target.location.hash.replace("#/", "");
const currentPanelId = this.currentActivePanel.id;
if (newPanelId !== currentPanelId) {
const newTabId = newPanelId.split("_panel").shift();
this.simulateClick(newTabId, newPanelId);
}
this._activateFromWindowLocationOrDefaultPanelTarget();
});
}
simulateClick(tab, panel) {
this.updateActivePanel(panel);
this.updateActiveTab(tab);
}
changeActivePanel(event) {
this.updateActivePanel(`${event.currentTarget.id}_panel`);
}
updateActivePanel(panel_id) {
const newActivePanel = this.panelTargets.find((panel) => panel.id == panel_id);
if (newActivePanel === undefined) {
// No panel found
return;
_activateFromWindowLocationOrDefaultPanelTarget() {
// Conveniently AngularJs rewrite "example.com#panel" to "example.com#/panel"
const hashWithoutSlash = window.location.hash.replace("/", "");
const tabWithSameHash = this.tabTargets.find((tab) => tab.hash == hashWithoutSlash);
if (hashWithoutSlash != "" && tabWithSameHash) {
this._activateByHash(tabWithSameHash.hash);
} else {
this._activateByHash(`#${this.defaultTarget.id}`);
}
this.currentActivePanel.style.display = "none";
newActivePanel.style.display = "block";
}
changeActiveTab(event) {
this.currentActiveTab.classList.remove(`${this.classNameValue}`);
event.currentTarget.classList.add(`${this.classNameValue}`);
activate(event) {
this._activateByHash(event.currentTarget.hash);
}
updateActiveTab(tab_id) {
const newActiveTab = this.tabTargets.find((tab) => tab.id == tab_id);
if (newActiveTab === undefined) {
// No tab found
return;
}
this.currentActiveTab.classList.remove(`${this.classNameValue}`);
newActiveTab.classList.add(`${this.classNameValue}`);
activateDefaultPanel() {
this._activateByHash(`#${this.defaultTarget.id}`);
}
get currentActiveTab() {
return this.tabTargets.find((tab) => tab.classList.contains("selected"));
}
get currentActivePanel() {
return this.panelTargets.find((panel) => panel.id == `${this.currentActiveTab.id}_panel`);
_activateByHash(hash) {
this.tabTargets.forEach((tab) => {
if (tab.hash == hash) {
tab.classList.add(this.classNameValue);
} else {
tab.classList.remove(this.classNameValue);
}
});
this.panelTargets.forEach((panel) => {
if (panel.id == hash.replace("#", "")) {
panel.style.display = "block";
} else {
panel.style.display = "none";
}
});
}
}

View File

@@ -11,122 +11,143 @@ describe('TabsAndPanelsController', () => {
application.register('tabs-and-panels', tabs_and_panels_controller);
});
describe('#tabs-and-panels', () => {
const checkDefaultPanel = () => {
const peekPanel = document.getElementById('peek_panel');
const kaPanel = document.getElementById('ka_panel');
const booPanel = document.getElementById('boo_panel');
beforeEach(() => {
document.body.innerHTML = `
<div data-controller="tabs-and-panels" data-tabs-and-panels-class-name-value="selected" data-action="orderCycleSelected@window->tabs-and-panels#activateDefaultPanel">
<a id="peek_tab" href="#peek_panel" data-action="tabs-and-panels#activate" class="selected" data-tabs-and-panels-target="tab">Peek</a>
<a id="ka_tab" href="#ka_panel" data-action="tabs-and-panels#activate" data-tabs-and-panels-target="tab">Ka</a>
<a id="boo_tab" href="#boo_panel" data-action="tabs-and-panels#activate" data-tabs-and-panels-target="tab">Boo</a>
expect(peekPanel.style.display).toBe('block');
expect(kaPanel.style.display).toBe('none');
expect(booPanel.style.display).toBe('none');
}
<div id="peek_panel" data-tabs-and-panels-target="panel default">Peek me</div>
<div id="ka_panel" data-tabs-and-panels-target="panel">Ka you</div>
<div id="boo_panel" data-tabs-and-panels-target="panel">Boo three</div>
</div>`
})
beforeEach(() => {
document.body.innerHTML = `
<div data-controller="tabs-and-panels" data-tabs-and-panels-class-name-value="selected">
<a id="peek" href="#" data-action="tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab" class="selected" data-tabs-and-panels-target="tab">Peek</a>
<a id="ka" href="#" data-action="tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab" data-tabs-and-panels-target="tab">Ka</a>
<a id="boo" href="#" data-action="tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab" data-tabs-and-panels-target="tab">Boo</a>
it("#activate by clicking on tab", () => {
const peakTab = document.getElementById("peek_tab")
const kaTab = document.getElementById("ka_tab")
const booTab = document.getElementById("boo_tab")
const peakPanel = document.getElementById("peek_panel")
const kaPanel = document.getElementById("ka_panel")
const booPanel = document.getElementById("boo_panel")
expect(peakTab.classList.contains("selected")).toBe(true)
expect(kaTab.classList.contains("selected")).toBe(false)
expect(booTab.classList.contains("selected")).toBe(false)
<div id="peek_panel" data-tabs-and-panels-target="panel default">Peek me</div>
<div id="ka_panel" data-tabs-and-panels-target="panel">Ka you</div>
<div id="boo_panel" data-tabs-and-panels-target="panel">Boo three</div>
</div>`;
});
expect(peakPanel.style.display).toBe("block")
expect(kaPanel.style.display).toBe("none")
expect(booPanel.style.display).toBe("none")
it('displays only the default panel', () => {
checkDefaultPanel()
});
kaTab.click()
describe('when tab is clicked', () => {
let ka;
expect(peakTab.classList.contains("selected")).toBe(false)
expect(kaTab.classList.contains("selected")).toBe(true)
expect(booTab.classList.contains("selected")).toBe(false)
beforeEach(() => {
ka = document.getElementById('ka');
})
expect(peakPanel.style.display).toBe("none")
expect(kaPanel.style.display).toBe("block")
expect(booPanel.style.display).toBe("none")
})
it('displays appropriate panel', () => {
const kaPanel = document.getElementById('ka_panel');
it("#activateDefaultPanel on orderCycleSelected event", () => {
const peakTab = document.getElementById("peek_tab")
const kaTab = document.getElementById("ka_tab")
const booTab = document.getElementById("boo_tab")
const peakPanel = document.getElementById("peek_panel")
const kaPanel = document.getElementById("ka_panel")
const booPanel = document.getElementById("boo_panel")
expect(kaPanel.style.display).toBe('none');
ka.click();
expect(kaPanel.style.display).toBe('block');
});
expect(peakTab.classList.contains("selected")).toBe(true)
expect(kaTab.classList.contains("selected")).toBe(false)
expect(booTab.classList.contains("selected")).toBe(false)
it('selects the clicked tab', () => {
ka.click();
expect(ka.classList.contains('selected')).toBe(true);
});
expect(peakPanel.style.display).toBe("block")
expect(kaPanel.style.display).toBe("none")
expect(booPanel.style.display).toBe("none")
describe("when panel doesn't exist", () => {
beforeEach(() => {
document.body.innerHTML = `
<div data-controller="tabs-and-panels" data-tabs-and-panels-class-name-value="selected">
<a id="peek" href="#" data-action="tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab" class="selected" data-tabs-and-panels-target="tab">Peek</a>
<a id="ka" href="#" data-action="tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab" data-tabs-and-panels-target="tab">Ka</a>
<a id="boo" href="#" data-action="tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab" data-tabs-and-panels-target="tab">Boo</a>
kaTab.click()
expect(peakTab.classList.contains("selected")).toBe(false)
expect(kaTab.classList.contains("selected")).toBe(true)
expect(booTab.classList.contains("selected")).toBe(false)
<div id="peek_panel" data-tabs-and-panels-target="panel default">Peek me</div>
<div id="boo_panel" data-tabs-and-panels-target="panel">Boo three</div>
</div>`;
});
expect(peakPanel.style.display).toBe("none")
expect(kaPanel.style.display).toBe("block")
expect(booPanel.style.display).toBe("none")
it('displays the current panel', () => {
const peekPanel = document.getElementById('peek_panel');
const event = new Event("orderCycleSelected")
window.dispatchEvent(event);
ka.click();
expect(peekPanel.style.display).toBe('block');
})
expect(peakTab.classList.contains("selected")).toBe(true)
expect(kaTab.classList.contains("selected")).toBe(false)
expect(booTab.classList.contains("selected")).toBe(false)
expect(peakPanel.style.display).toBe("block")
expect(kaPanel.style.display).toBe("none")
expect(booPanel.style.display).toBe("none")
})
describe("when valid anchor is specified in the url", () => {
const oldWindowLocation = window.location
beforeAll(() => {
Object.defineProperty(window, "location", {
value: new URL("http://example.com/#boo_panel"),
configurable: true,
})
})
afterAll(() => {
delete window.location
window.location = oldWindowLocation
})
describe('when anchor is specified in the url', () => {
const { location } = window;
const mockLocationToString = (panel) => {
// Mocking window.location.toString()
const url = `http://localhost:3000/admin/enterprises/great-shop/edit#/${panel}`
const mockedToString = jest.fn()
mockedToString.mockImplementation(() => (url))
it("#activateFromWindowLocationOrDefaultPanelTarget show panel based on anchor", () => {
const peakTab = document.getElementById("peek_tab")
const kaTab = document.getElementById("ka_tab")
const booTab = document.getElementById("boo_tab")
const peakPanel = document.getElementById("peek_panel")
const kaPanel = document.getElementById("ka_panel")
const booPanel = document.getElementById("boo_panel")
delete window.location
window.location = {
toString: mockedToString
}
}
expect(peakTab.classList.contains("selected")).toBe(false)
expect(kaTab.classList.contains("selected")).toBe(false)
expect(booTab.classList.contains("selected")).toBe(true)
beforeAll(() => {
mockLocationToString('ka_panel')
})
expect(peakPanel.style.display).toBe("none")
expect(kaPanel.style.display).toBe("none")
expect(booPanel.style.display).toBe("block")
})
})
afterAll(() => {
// cleaning up
window.location = location
})
it('displays the panel associated with the anchor', () => {
const kaPanel = document.getElementById('ka_panel');
expect(kaPanel.style.display).toBe('block');
})
it('selects the tab entry associated with the anchor', () => {
const ka = document.getElementById('ka');
expect(ka.classList.contains('selected')).toBe(true);
})
describe("when anchor doesn't macht any panel", () => {
beforeAll(() => {
mockLocationToString('random_panel')
})
it('displays the default panel', () => {
checkDefaultPanel()
})
describe("when non valid anchor is specified in the url", () => {
const oldWindowLocation = window.location
beforeAll(() => {
Object.defineProperty(window, "location", {
value: new URL("http://example.com/#non_valid_panel"),
configurable: true,
})
})
});
});
afterAll(() => {
delete window.location
window.location = oldWindowLocation
})
it("#activateFromWindowLocationOrDefaultPanelTarget show default panel", () => {
const peakTab = document.getElementById("peek_tab")
const kaTab = document.getElementById("ka_tab")
const booTab = document.getElementById("boo_tab")
const peakPanel = document.getElementById("peek_panel")
const kaPanel = document.getElementById("ka_panel")
const booPanel = document.getElementById("boo_panel")
expect(peakTab.classList.contains("selected")).toBe(true)
expect(kaTab.classList.contains("selected")).toBe(false)
expect(booTab.classList.contains("selected")).toBe(false)
expect(peakPanel.style.display).toBe("block")
expect(kaPanel.style.display).toBe("none")
expect(booPanel.style.display).toBe("none")
})
})
})

View File

@@ -131,25 +131,25 @@ describe '
# Unchecking hides the Properties tab
uncheck 'enterprise_is_primary_producer'
choose 'None'
expect(page).not_to have_selector "#enterprise_fees"
expect(page).not_to have_selector "#payment_methods"
expect(page).not_to have_selector "#shipping_methods"
expect(page).not_to have_selector "#properties"
expect(page).not_to have_selector "[data-test=link_for_enterprise_fees]"
expect(page).not_to have_selector "[data-test=link_for_payment_methods]"
expect(page).not_to have_selector "[data-test=link_for_shipping_methods]"
expect(page).not_to have_selector "[data-test=link_for_properties]"
# Checking displays the Properties tab
check 'enterprise_is_primary_producer'
expect(page).to have_selector "#enterprise_fees"
expect(page).not_to have_selector "#payment_methods"
expect(page).not_to have_selector "#shipping_methods"
expect(page).to have_selector "#properties"
expect(page).to have_selector "[data-test=link_for_enterprise_fees]"
expect(page).not_to have_selector "[data-test=link_for_payment_methods]"
expect(page).not_to have_selector "[data-test=link_for_shipping_methods]"
expect(page).to have_selector "[data-test=link_for_properties]"
uncheck 'enterprise_is_primary_producer'
choose 'Own'
expect(page).to have_selector "#enterprise_fees"
expect(page).to have_selector "#payment_methods"
expect(page).to have_selector "#shipping_methods"
expect(page).to have_selector "[data-test=link_for_enterprise_fees]"
expect(page).to have_selector "[data-test=link_for_payment_methods]"
expect(page).to have_selector "[data-test=link_for_shipping_methods]"
choose 'Any'
expect(page).to have_selector "#enterprise_fees"
expect(page).to have_selector "#payment_methods"
expect(page).to have_selector "#shipping_methods"
expect(page).to have_selector "[data-test=link_for_enterprise_fees]"
expect(page).to have_selector "[data-test=link_for_payment_methods]"
expect(page).to have_selector "[data-test=link_for_shipping_methods]"
page.find("#enterprise_group_ids-ts-control").set(eg1.name)
page.find("#enterprise_group_ids-ts-dropdown .option.active").click