diff --git a/src/pages/sponsors/__tests__/edit-sponsor-page.test.js b/src/pages/sponsors/__tests__/edit-sponsor-page.test.js new file mode 100644 index 000000000..f6a1367d7 --- /dev/null +++ b/src/pages/sponsors/__tests__/edit-sponsor-page.test.js @@ -0,0 +1,198 @@ +import React from "react"; +import userEvent from "@testing-library/user-event"; +import { act, screen } from "@testing-library/react"; +import EditSponsorPage, { + getFragmentFromValue, + getTabFromUrlFragment +} from "../edit-sponsor-page"; +import { renderWithRedux } from "../../../utils/test-utils"; +import { DEFAULT_STATE as currentSponsorDefaultState } from "../../../reducers/sponsors/sponsor-reducer"; +import { + DEFAULT_ENTITY as defaultSummitEntity, + DEFAULT_STATE as currentSummitDefaultState +} from "../../../reducers/summits/current-summit-reducer"; + +global.window = { location: { pathname: "/sponsor-forms/items" } }; +jest.mock( + "../sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items.js" +); +jest.mock("../sponsor-users-list-per-sponsor/index.js"); + +describe("EditSponsorPage", () => { + describe("getFragmentFromValue", () => { + it("returns correct values", () => { + const result1 = getFragmentFromValue(0); + expect(result1).toBe("general"); + + const result2 = getFragmentFromValue(2); + expect(result2).toBe("pages"); + + const result3 = getFragmentFromValue(3); + expect(result3).toBe("media_uploads"); + + const result4 = getFragmentFromValue(7); + expect(result4).toBe("badge_scans"); + }); + }); + + describe("getTabFromUrlFragment", () => { + it("returns correct values for defined fragments", () => { + const newUrl1 = "#general"; + window.location.hash = newUrl1; + + const result1 = getTabFromUrlFragment(); + expect(result1).toBe(0); + + const newUrl2 = "#pages"; + window.location.hash = newUrl2; + + const result2 = getTabFromUrlFragment(); + expect(result2).toBe(2); + + const newUrl3 = "#media_uploads"; + window.location.hash = newUrl3; + + const result3 = getTabFromUrlFragment(); + expect(result3).toBe(3); + + const newUrl4 = "#badge_scans"; + window.location.hash = newUrl4; + + const result4 = getTabFromUrlFragment(); + expect(result4).toBe(7); + }); + }); + + describe("Component", () => { + const originalWindowLocation = window.location; + it("should change the url fragment on tab click", async () => { + delete window.location; + + Object.defineProperty(window, "location", { + configurable: true, + writable: true, + value: { + ...originalWindowLocation, + hash: "#general" + } + }); + + renderWithRedux( + , + { + initialState: { + currentSummitState: { + currentSummit: defaultSummitEntity, + ...currentSummitDefaultState + }, + loggedUserState: { + member: { + groups: {} + } + }, + currentSummitSponsorshipListState: { + sponsorships: [], + currentPage: 1, + lastPage: 1, + perPage: 100, + order: "order", + orderDir: 1, + totalSponsorships: 0 + }, + currentSponsorState: { + sponsorships: [], + ...currentSponsorDefaultState + } + } + } + ); + + const usersTabReference = screen.getByText("edit_sponsor.tab.forms"); + + await act(async () => { + await userEvent.click(usersTabReference); + }); + + expect(window.location.hash).toBe("forms"); + }); + + it("should change the tab rendered on fragment change", async () => { + delete window.location; + + Object.defineProperty(window, "location", { + configurable: true, + writable: true, + value: { + ...originalWindowLocation, + hash: "#general" + } + }); + + renderWithRedux( + , + { + initialState: { + currentSummitState: { + currentSummit: defaultSummitEntity, + ...currentSummitDefaultState + }, + loggedUserState: { + member: { + groups: {} + } + }, + currentSummitSponsorshipListState: { + sponsorships: [], + currentPage: 1, + lastPage: 1, + perPage: 100, + order: "order", + orderDir: 1, + totalSponsorships: 0 + }, + currentSponsorState: { + sponsorships: [], + ...currentSponsorDefaultState + } + } + } + ); + + const generalTabPanel = screen.getByTestId("simple-tabpanel-0"); + expect(generalTabPanel).toBeDefined(); + + delete window.location; + + Object.defineProperty(window, "location", { + configurable: true, + writable: true, + value: { + ...originalWindowLocation, + hash: "#users" + } + }); + + const usersTabPanel = screen.getByTestId("simple-tabpanel-1"); + expect(usersTabPanel).toBeDefined(); + }); + + afterEach(() => { + Object.defineProperty(window, "location", { + configurable: true, + value: originalWindowLocation + }); + }); + }); +}); diff --git a/src/pages/sponsors/edit-sponsor-page.js b/src/pages/sponsors/edit-sponsor-page.js index 3a4e9a709..d3101dc25 100644 --- a/src/pages/sponsors/edit-sponsor-page.js +++ b/src/pages/sponsors/edit-sponsor-page.js @@ -46,7 +46,27 @@ import SponsorCartTab from "./sponsor-cart-tab"; import SponsorFormsManageItems from "./sponsor-forms-tab/components/manage-items/sponsor-forms-manage-items"; import { SPONSOR_TABS } from "../../utils/constants"; -const CustomTabPanel = (props) => { +export const tabsToFragmentMap = [ + "general", + "users", + "pages", + "media_uploads", + "forms", + "cart", + "purchases", + "badge_scans" +]; + +export const getFragmentFromValue = (index) => tabsToFragmentMap[index]; + +export const getTabFromUrlFragment = () => { + const currentHash = window.location.hash.replace("#", ""); + const result = tabsToFragmentMap.indexOf(currentHash); + if (result > -1) return result; + return 0; +}; + +export const CustomTabPanel = (props) => { const { children, value, index, ...other } = props; return ( @@ -54,6 +74,7 @@ const CustomTabPanel = (props) => { role="tabpanel" hidden={value !== index} id={`simple-tabpanel-${index}`} + data-testid={`simple-tabpanel-${index}`} aria-labelledby={`simple-tab-${index}`} {...other} > @@ -97,18 +118,21 @@ const EditSponsorPage = (props) => { getExtraQuestionMeta } = props; - const [selectedTab, setSelectedTab] = useState( - location.pathname.includes("/sponsor-forms/") && - location.pathname.includes("/items") - ? SPONSOR_TABS.FORMS - : 0 - ); + const [selectedTab, setSelectedTab] = useState(getTabFromUrlFragment()); const handleTabChange = (event, newValue) => { setSelectedTab(newValue); - history.push(`/app/summits/${currentSummit.id}/sponsors/${entity.id}`); + window.location.hash = getFragmentFromValue(newValue); }; + useEffect(() => { + const onHashChange = () => setSelectedTab(getTabFromUrlFragment()); + window.addEventListener("hashchange", onHashChange); + // default call + if (!window.location.hash) handleTabChange(null, getTabFromUrlFragment()); + return () => window.removeEventListener("hashchange", onHashChange); + }, []); + useEffect(() => { if (entity.id > 0) { getSponsorAdvertisements(entity.id); diff --git a/src/reducers/sponsors/sponsor-reducer.js b/src/reducers/sponsors/sponsor-reducer.js index ff98dd6e2..142bca97e 100644 --- a/src/reducers/sponsors/sponsor-reducer.js +++ b/src/reducers/sponsors/sponsor-reducer.js @@ -132,7 +132,7 @@ export const DEFAULT_ENTITY = { sponsorships_collection: DEFAULT_SPONSORHIPS_STATE }; -const DEFAULT_STATE = { +export const DEFAULT_STATE = { entity: DEFAULT_ENTITY, selectedSponsorship: null, errors: {} diff --git a/src/reducers/summits/current-summit-reducer.js b/src/reducers/summits/current-summit-reducer.js index 88d7cc951..3e229b606 100644 --- a/src/reducers/summits/current-summit-reducer.js +++ b/src/reducers/summits/current-summit-reducer.js @@ -214,7 +214,7 @@ const DEFAULT_PRINT_APP_MARKETING_SETTINGS = { PRINT_APP_HIDE_FIND_TICKET_BY_FULLNAME: { id: 0, value: false } }; -const DEFAULT_STATE = { +export const DEFAULT_STATE = { currentSummit: DEFAULT_ENTITY, errors: {}, loading: true, diff --git a/yarn.lock b/yarn.lock index e3e15533f..3b62ca6e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11048,16 +11048,7 @@ prop-types-extra@^1.0.1: react-is "^16.3.2" warning "^4.0.0" -prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - -prop-types@^15.8.1: +prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==