diff --git a/creativecloud/blocks/sidenav/sidenav.js b/creativecloud/blocks/sidenav/sidenav.js index ac16ca7c6..b3e388d5d 100644 --- a/creativecloud/blocks/sidenav/sidenav.js +++ b/creativecloud/blocks/sidenav/sidenav.js @@ -2,31 +2,43 @@ import { createTag, getLibs } from '../../scripts/utils.js'; import '../../deps/merch-sidenav.js'; -const CATEGORY_TYPE = 'Categories'; -const TYPE_TYPE = 'Types'; -const getValueFromLabel = (content) => content - .trim() - .toLowerCase() - .replace(/( and )/g, ' ') - .replace(/-/g, '') - .replace(/\s+/g, '-'); +const CATEGORY_ID_PREFIX = 'categories/'; +const TYPE_ID_PREFIX = 'types/'; -const getCategories = (arrayCategories) => { - const tag = createTag('sp-sidenav', { variant: 'multilevel', manageTabIndex: true }); +const getIdLeaf = (id) => (id?.substring(id.lastIndexOf('/') + 1) || id); + +const getCategories = (items, isMultilevel, mapCategories) => { + const configuration = { manageTabIndex: true }; + if (isMultilevel) { + configuration.variant = 'multilevel'; + } + const mapParents = []; + const tag = createTag('sp-sidenav', configuration); const merchTag = createTag('merch-sidenav-list', { deeplink: 'category' }); merchTag.append(tag); - const mapParents = {}; - arrayCategories.forEach((item) => { - if (item.Name?.length > 0) { - const parentName = item.Name.split('|')[0].trim(); - if (!mapParents[parentName]) { - mapParents[parentName] = createTag('sp-sidenav-item', { label: parentName, value: getValueFromLabel(parentName) }); - tag.append(mapParents[parentName]); - } - const childName = item.Name.split('|')[1]?.trim(); - if (childName) { - const childNode = createTag('sp-sidenav-item', { label: childName, value: getValueFromLabel(childName) }); - mapParents[parentName].append(childNode); + items.forEach((item) => { + if (item) { + let parent = tag; + const value = getIdLeaf(item.id); + // first token is type, second is parent category + const isParent = item.id.split('/').length <= 2; + const itemTag = createTag('sp-sidenav-item', { label: item.name, value }); + if (isParent) { + mapParents[value] = itemTag; + tag.append(itemTag); + } else { + const parentId = getIdLeaf(item.id.substring(0, item.id.lastIndexOf('/'))); + if (isMultilevel) { + if (!mapParents[parentId]) { + const parentItem = mapCategories[parentId]; + if (parentItem) { + mapParents[parentId] = createTag('sp-sidenav-item', { label: parentItem.name, parentId }); + tag.append(mapParents[parentId]); + } + } + parent = mapParents[parentId]; + } + parent?.append(itemTag); } } }); @@ -36,28 +48,52 @@ const getCategories = (arrayCategories) => { const getTypes = (arrayTypes) => { const tag = createTag('merch-sidenav-checkbox-group', { title: 'Types', deeplink: 'types' }); arrayTypes.forEach((item) => { - if (item.Name?.length > 0) { - const checkbox = createTag('sp-checkbox', { emphasized: '', name: getValueFromLabel(item.Name) }); - checkbox.append(document.createTextNode(item.Name)); + if (item.name?.length > 0) { + const checkbox = createTag('sp-checkbox', { + emphasized: '', + name: getIdLeaf(item.id), + }); + checkbox.append(item.name); tag.append(checkbox); } }); return tag; }; -const appendFilters = async (root, link) => { +const appendFilters = async (root, link, explicitCategoriesElt) => { const payload = link.textContent.trim(); try { const resp = await fetch(payload); if (resp.ok) { const json = await resp.json(); - const arrayCategories = json.data.filter((item) => item.Type === CATEGORY_TYPE); - if (arrayCategories.length > 0) { - root.append(getCategories(arrayCategories)); + const mapCategories = {}; + let categoryValues = []; + const types = []; + json.data.forEach((item) => { + if (item.id?.startsWith(CATEGORY_ID_PREFIX)) { + const value = getIdLeaf(item.id); + mapCategories[value] = item; + categoryValues.push(value); + } else if (item.id?.startsWith(TYPE_ID_PREFIX)) { + types.push(item); + } + }); + if (explicitCategoriesElt) { + categoryValues = Array.from(explicitCategoriesElt.querySelectorAll('li')) + .map((item) => item.textContent.trim().toLowerCase()); + } + let shallowCategories = true; + if (categoryValues.length > 0) { + const items = categoryValues.map((value) => mapCategories[value]); + const parentValues = new Set(items.map((value) => value?.id.split('/')[1])); + // all parent will always be here without children, + // so shallow is considered below 2 parents + shallowCategories = parentValues.size <= 2; + const categoryTags = getCategories(items, !shallowCategories, mapCategories); + root.append(categoryTags); } - const arrayTypes = json.data.filter((item) => item.Type === TYPE_TYPE); - if (arrayTypes.length > 0) { - root.append(getTypes(arrayTypes)); + if (!shallowCategories && types.length > 0) { + root.append(getTypes(types)); } } } catch (e) { @@ -104,10 +140,11 @@ export default async function init(el) { const title = el.querySelector('h2')?.textContent.trim(); const rootNav = createTag('merch-sidenav', { title }); - const searchText = el.querySelector('p')?.textContent.trim(); + const searchText = el.querySelector('p > strong')?.textContent.trim(); appendSearch(rootNav, searchText); const links = el.querySelectorAll('a'); - await appendFilters(rootNav, links[0]); + const explicitCategories = el.querySelector('ul'); + await appendFilters(rootNav, links[0], explicitCategories); if (links.length > 1) { appendResources(rootNav, links[1]); } diff --git a/test/blocks/sidenav/mocks/sidenav.html b/test/blocks/sidenav/mocks/sidenav.html index 21b4f21e2..f3712df3f 100644 --- a/test/blocks/sidenav/mocks/sidenav.html +++ b/test/blocks/sidenav/mocks/sidenav.html @@ -14,17 +14,55 @@

SIDE NAV testing

REFINE YOUR RESULTS

-

Search all your products

+

Search all your products

https://www.adobe.com/some/taxonomy.json

RESOURCES: Special Offers

+
+
+
+

REFINE YOUR RESULTS

+

Search all your products

+

https://www.adobe.com/some/taxonomy.json

+
    +
  • all
  • +
  • Graphic-design
  • +
  • Photo
  • +
  • Illustration
  • +
  • Social-media
  • +
  • Pdf
  • +
  • Esignatures
  • +
  • Coldfusion
  • +
  • Elearning
  • +
  • Print-imaging
  • +
  • Technical-communication
  • +
  • Insight-audiences
  • +
  • Content-journeys
  • +
  • Channel-engagement
  • +
  • Commerce-conversions
  • +
  • Optimization-scale
  • +
+

RESOURCES: Special Offers

+
+
+
-

REFINE YOUR RESULTS (this is plans)

-

https://main--milo--adobecom.hlx.page/some/taxonomy.json/a>

+

REFINE YOUR RESULTS

+

https://www.adobe.com//some/taxonomy.json

+
    +
  • All
  • +
  • Photo
  • +
  • Graphic-design
  • +
  • Video
  • +
  • Illustration
  • +
  • Acrobat-pdf
  • +
  • 3d-ar
  • +
  • Social-media
  • +
diff --git a/test/blocks/sidenav/mocks/taxonomy.json b/test/blocks/sidenav/mocks/taxonomy.json index deffe42e6..4941c4a3a 100644 --- a/test/blocks/sidenav/mocks/taxonomy.json +++ b/test/blocks/sidenav/mocks/taxonomy.json @@ -1,169 +1,111 @@ { - "total": 27, + "total": 26, "offset": 0, - "limit": 27, + "limit": 26, "data": [ { - "Type": "Categories", - "Level 1": "", - "Level 2": "", - "Name": "" + "name": "All", + "id": "categories/all" }, { - "Type": "Categories", - "Level 1": "All", - "Level 2": "", - "Name": "All" + "name": "Creativity and Design", + "id": "categories/creativity-design" }, { - "Type": "Categories", - "Level 1": "Creativity and Design", - "Level 2": "", - "Name": "Creativity and Design" + "name": "Photo", + "id": "categories/creativity-design/photo" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Photo", - "Name": "Creativity and Design | Photo" + "name": "Graphic Design", + "id": "categories/creativity-design/graphic-design" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Graphic Design", - "Name": "Creativity and Design | Graphic Design" + "name": "Video", + "id": "categories/creativity-design/video" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Video", - "Name": "Creativity and Design | Video" + "name": "Illustration", + "id": "categories/creativity-design/illustration" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Illustration", - "Name": "Creativity and Design | Illustration" + "name": "Acrobat and PDF", + "id": "categories/creativity-design/acrobat-pdf" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Acrobat and PDF", - "Name": "Creativity and Design | Acrobat and PDF" + "name": "3D and AR", + "id": "categories/creativity-design/3d-ar" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "3D and AR", - "Name": "Creativity and Design | 3D and AR" + "name": "Social Media", + "id": "categories/creativity-design/social-media" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Social Media", - "Name": "Creativity and Design | Social Media" + "name": "PDF and E-signatures", + "id": "categories/pdf-esignatures" }, { - "Type": "Categories", - "Level 1": "PDF and E-signatures", - "Level 2": "", - "Name": "PDF and E-signatures" + "name": "PDF", + "id": "categories/pdf-esignatures/pdf" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "PDF", - "Name": "PDF and E-signatures | PDF" + "name": "E-signatures", + "id": "categories/pdf-esignatures/esignatures" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "E-signatures", - "Name": "PDF and E-signatures | E-signatures" + "name": "Marketing and Commerce", + "id": "categories/marketing-commerce" }, { - "Type": "Categories", - "Level 1": "Marketing and Commerce", - "Level 2": "", - "Name": "Marketing and Commerce" + "name": "Insights and Audiences", + "id": "categories/marketing-commerce/insights-audiences" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Insights and Audiences", - "Name": "Marketing and Commerce | Insights and Audiences" + "name": "Content and Journeys", + "id": "categories/marketing-commerce/content-journeys" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Content and Journeys", - "Name": "Marketing and Commerce | Content and Journeys" + "name": "Channels and Engagement", + "id": "categories/marketing-commerce/channels-engagement" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Channels and Engagement", - "Name": "Marketing and Commerce | Channels and Engagement" + "name": "Commerce and Conversions", + "id": "categories/marketing-commerce/commerce-conversions" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Commerce and Conversions", - "Name": "Marketing and Commerce | Commerce and Conversions" + "name": "Optimization and Scale", + "id": "categories/marketing-commerce/optimization-scale" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Optimization and Scale", - "Name": "Marketing and Commerce | Optimization and Scale" + "name": "Additional Solutions", + "id": "categories/additional-solutions" }, { - "Type": "Categories", - "Level 1": "Additional Solutions", - "Level 2": "", - "Name": "Additional Solutions" + "name": "ColdFusion", + "id": "categories/additional-solutions/coldFusion" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "ColdFusion", - "Name": "Additional Solutions | ColdFusion" + "name": "eLearning", + "id": "categories/additional-solutions/eLearning" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "eLearning", - "Name": "Additional Solutions | eLearning" + "name": "Print Imaging", + "id": "categories/additional-solutions/print-imaging" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Print Imaging", - "Name": "Additional Solutions | Print Imaging" + "name": "Technical Communication", + "id": "categories/additional-solutions/technical-communication" }, { - "Type": "Categories", - "Level 1": "", - "Level 2": "Technical Communication", - "Name": "Additional Solutions | Technical Communication" + "name": "Desktop", + "id": "types/desktop" }, { - "Type": "Types", - "Level 1": "Desktop", - "Level 2": "", - "Name": "Desktop" + "name": "Mobile", + "id": "types/mobile" }, { - "Type": "Types", - "Level 1": "Mobile", - "Level 2": "", - "Name": "Mobile" - }, - { - "Type": "Types", - "Level 1": "Web", - "Level 2": "", - "Name": "Web" + "name": "Web", + "id": "types/web" } ], ":type": "sheet" diff --git a/test/blocks/sidenav/sidenav.test.js b/test/blocks/sidenav/sidenav.test.js index 5577a5f92..0f97b9ec3 100644 --- a/test/blocks/sidenav/sidenav.test.js +++ b/test/blocks/sidenav/sidenav.test.js @@ -27,16 +27,37 @@ describe('Sidenav', () => { window.fetch = sinon.stub().callsFake(() => mockedTaxonomy()); }); - it('does create nice categories sidenav', async () => { - const sidenavEl = document.querySelector('.sidenav.categories'); - await init(sidenavEl); - const newRoot = document.querySelector('merch-sidenav'); - expect(newRoot.title).to.equal("REFINE YOUR RESULTS"); + const testCategorySidenav = async (selector, expectedItemCount, expectedChildItemCount) => { + const sidenavEl = document.querySelector(selector); + const newRoot = await init(sidenavEl); + expect(newRoot.tagName).to.equal('MERCH-SIDENAV'); + expect(newRoot.title).to.equal('REFINE YOUR RESULTS'); const items = newRoot.querySelectorAll('sp-sidenav-item'); - expect(items.length).to.equal(24); + expect(items.length).to.equal(expectedItemCount); const search = newRoot.querySelector('sp-search'); expect(search.getAttribute('placeholder')).to.equal('Search all your products'); expect(newRoot.querySelectorAll('sp-checkbox').length).to.equal(3); + const nestedItems = newRoot.querySelectorAll('sp-sidenav-item > sp-sidenav-item'); + expect(nestedItems.length).to.equal(expectedChildItemCount); expect(newRoot.querySelector('sp-checkbox').textContent.trim()).to.equal('Desktop'); + }; + + it('does create nice categories default sidenav', async () => { + await testCategorySidenav('.categories', 24, 18); + }); + + it('does create nice reordered categories sidenav', async () => { + await testCategorySidenav('.reordered-categories', 17, 11); + }); + + it('does create nice plans sidenav', async () => { + const sidenavEl = document.querySelector('.plans'); + const newRoot = await init(sidenavEl); + expect(newRoot.tagName).to.equal('MERCH-SIDENAV'); + expect(newRoot.title).to.equal('REFINE YOUR RESULTS'); + const search = newRoot.querySelector('sp-search'); + expect(search).to.be.null; + const nestedItems = newRoot.querySelectorAll('sp-sidenav-item > sp-sidenav-item'); + expect(nestedItems.length).to.equal(0); }); });