From 167139042ff647336e46009a5752d633f2793730 Mon Sep 17 00:00:00 2001 From: mdickson-adbe <95774602+mdickson-adbe@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:52:52 -0400 Subject: [PATCH] Performance refactoring (#107) * performance refactoring - improve thumbnail performance by makin async - format date property to yyyy-mm-dd - fix margin issue in program header - refactor function that builds header dropdown for increased performance * resolve bug with event listeners not attaching --- .../gmo-program-header/gmo-program-header.css | 2 +- .../gmo-program-header/gmo-program-header.js | 43 ++++++++-------- blocks/gmo-program-list/gmo-program-list.css | 17 +++++-- blocks/gmo-program-list/gmo-program-list.js | 50 ++++++++++++------- scripts/graphql.js | 30 ----------- 5 files changed, 64 insertions(+), 78 deletions(-) diff --git a/blocks/gmo-program-header/gmo-program-header.css b/blocks/gmo-program-header/gmo-program-header.css index ac30d581..e00c20fb 100644 --- a/blocks/gmo-program-header/gmo-program-header.css +++ b/blocks/gmo-program-header/gmo-program-header.css @@ -1,4 +1,4 @@ -.gmo-campaign-header.block { +.gmo-program-header.block { display: flex; flex-direction: column; margin-top: 20px; diff --git a/blocks/gmo-program-header/gmo-program-header.js b/blocks/gmo-program-header/gmo-program-header.js index 98e15246..d7a9e46d 100644 --- a/blocks/gmo-program-header/gmo-program-header.js +++ b/blocks/gmo-program-header/gmo-program-header.js @@ -2,9 +2,6 @@ import { decorateIcons } from '../../scripts/lib-franklin.js'; import { graphqlQueryNameList, graphqlCampaignByName } from '../../scripts/graphql.js'; import { statusMapping, productList } from '../../scripts/shared-program.js'; -// Declared at the top of the file, making it accessible to all functions within this file. -let allProducts = []; - export default async function decorate(block) { block.innerHTML = `
@@ -128,32 +125,29 @@ export default async function decorate(block) { sendGmoCampaignListBlockEvent(); }); - await initializeDropdowns(); - attachEventListeners(); + initializeDropdowns(); decorateIcons(block); + document.addEventListener('click', handleClickOutside); } async function initializeDropdowns() { // Business Line List + graphqlQueryNameList('getBusinessLine').then((response) => { + populateDropdown(response, 'dropdownBusinessOptions', 'businessLine'); + }); - const businessLineResponse = await graphqlQueryNameList('getBusinessLine'); - const businessLines = businessLineResponse.data.jsonByPath.item.json.options; - populateDropdown(businessLines, 'dropdownBusinessOptions', 'businessLine'); + // Geo List + graphqlQueryNameList('getGeoList').then((response) => { + populateDropdown(response, 'dropdownGeoOptions', 'p0TargetGeo'); + }); // Status List const statusResponse = await statusMapping; - const statuses = statusResponse.data.jsonByPath.item.json.options; - populateDropdown(statuses, 'dropdownStatusOptions', 'status'); + populateDropdown(statusResponse, 'dropdownStatusOptions', 'status'); // Product List const productResponse = await productList; - allProducts = productResponse.data.jsonByPath.item.json.options; - populateDropdown(allProducts, 'dropdownProductOptions', 'productOffering'); - - // Geo List - const geoResponse = await graphqlQueryNameList('getGeoList'); - const geos = geoResponse.data.jsonByPath.item.json.options; - populateDropdown(geos, 'dropdownGeoOptions', 'p0TargetGeo'); + populateDropdown(productResponse, 'dropdownProductOptions', 'productOffering'); } // Function to attach event listeners @@ -173,12 +167,10 @@ function attachEventListeners() { if (resetFiltersBtn) { resetFiltersBtn.addEventListener('click', resetFiltersClickHandler); } - - // Add event listener for clicks outside of dropdowns - document.addEventListener('click', handleClickOutside); } -function populateDropdown(options, dropdownId, type) { +function populateDropdown(response, dropdownId, type) { + const options = response.data?.jsonByPath ? response.data.jsonByPath.item.json.options : response; let dropdownContent = document.getElementById(dropdownId); dropdownContent.innerHTML = ''; options.forEach((option, index) => { @@ -189,13 +181,18 @@ function populateDropdown(options, dropdownId, type) { anchor.dataset.type = type; anchor.className = "dropoption"; anchor.textContent = option.text; + anchor.addEventListener('click', dropOptionClickHandler); dropdownContent.appendChild(anchor); }); + // add event listener to button + const button = dropdownContent.parentElement.querySelector(".dropdown-button"); + button.addEventListener('click', dropdownButtonClickHandler); } // Function to filter products based on selected business line function filterProductsByBusinessLine(businessLine) { - const filteredProducts = allProducts.filter(product => + const products = productList.data.jsonByPath.item.json.options; + const filteredProducts = products.filter(product => product['business-line'].includes(businessLine) ); populateDropdown(filteredProducts, 'dropdownProductOptions', 'productOffering'); @@ -324,7 +321,7 @@ function resetAllFilters() { function resetProductsDropDown(){ // Populate all products into Products dropdown - populateDropdown(allProducts, 'dropdownProductOptions', 'productOffering'); + populateDropdown(productList, 'dropdownProductOptions', 'productOffering'); // Reset product offering filters removeSelectedProductOfferingFilters(); attachEventListeners(); diff --git a/blocks/gmo-program-list/gmo-program-list.css b/blocks/gmo-program-list/gmo-program-list.css index 7ab7b32e..a417c0c9 100644 --- a/blocks/gmo-program-list/gmo-program-list.css +++ b/blocks/gmo-program-list/gmo-program-list.css @@ -26,9 +26,20 @@ body { display: flex; font-weight: bold; margin-bottom: 20px; - + position: sticky; padding-top: 5px; } +.list-items { + display: flex; + flex-direction: column; + overflow-y: scroll; + height: 65vh; + -ms-overflow-style: none; + scrollbar-width: none; + &::-webkit-scrollbar { + display: none; + } +} .column-header-wrapper { display: flex; } @@ -48,10 +59,6 @@ body { height: 14px; width: 14px; } -.list-items { - display: flex; - flex-direction: column; -} .campaign-row { display: flex; font-size: 14px; diff --git a/blocks/gmo-program-list/gmo-program-list.js b/blocks/gmo-program-list/gmo-program-list.js index 71806d5f..b86c0396 100644 --- a/blocks/gmo-program-list/gmo-program-list.js +++ b/blocks/gmo-program-list/gmo-program-list.js @@ -150,6 +150,10 @@ async function buildCampaignList(campaigns, numPerPage) { for (const campaign of campaigns) { const index = campaigns.indexOf(campaign); const campaignRow = document.createElement('div'); + const programName = campaign.node.programName; + const campaignName = campaign.node.campaignName; + const programRef = campaign.node.programReferenceNumber; + campaignRow.classList.add('campaign-row'); if ((index + 1) > numPerPage) campaignRow.classList.add('hidden'); @@ -163,35 +167,27 @@ async function buildCampaignList(campaigns, numPerPage) { const campaignIcon = document.createElement('div'); campaignIcon.classList.add('campaign-icon'); - campaignIcon.dataset.programname = campaign.node.programName; - campaignIcon.dataset.campaignname = campaign.node.campaignName; - //Add Icon Image - const iconImage = document.createElement('img'); - try { - const imageObject = await searchAsset(campaign.node.programName, campaign.node.campaignName); - iconImage.src = imageObject.imageUrl; - iconImage.alt = imageObject.imageAltText; - } catch (error) { - } - // Append the image to the campaignIcon div - campaignIcon.appendChild(iconImage); + campaignIcon.dataset.programname = programName; + campaignIcon.dataset.campaignname = campaignName; + campaignIcon.dataset.reference = programRef; + addThumbnail(campaignIcon, programName, campaignName); campaignIconLink.appendChild(campaignIcon); - const campaignName = document.createElement('div'); - campaignName.classList.add('campaign-name-wrapper', 'vertical-center'); + const campaignNameWrapper = document.createElement('div'); + campaignNameWrapper.classList.add('campaign-name-wrapper', 'vertical-center'); - campaignName.innerHTML = ` + campaignNameWrapper.innerHTML = `
- ${checkBlankString(campaign.node.programName)} + ${checkBlankString(programName)} Program Name
- ${checkBlankString(campaign.node.campaignName,'Marketing Moment Not Available')} + ${checkBlankString(campaignName,'Marketing Moment Not Available')} Marketing Moment
`; campaignInfoWrapper.appendChild(campaignIconLink); - campaignInfoWrapper.appendChild(campaignName); + campaignInfoWrapper.appendChild(campaignNameWrapper); const campaignOverviewWrapper = document.createElement('div'); campaignOverviewWrapper.classList.add('column-2', 'campaign-description-wrapper', 'vertical-center'); @@ -203,7 +199,7 @@ async function buildCampaignList(campaigns, numPerPage) { campaignOverviewWrapper.appendChild(campaignOverview); const campaignLaunch = document.createElement('div'); - campaignLaunch.textContent = checkBlankString(campaign.node.launchDate); + campaignLaunch.textContent = dateFormat(campaign.node.launchDate); campaignLaunch.classList.add('column-3', 'campaign-launch-date', 'vertical-center'); campaignLaunch.dataset.property = 'launch'; @@ -238,6 +234,17 @@ function buildStatus(statusWrapper, campaign) { return statusWrapper; } +async function addThumbnail(parentElement, programName, campaignName) { + searchAsset(programName, campaignName).then((response) => { + if (response && (Object.hasOwn(response, 'imageUrl') && Object.hasOwn(response, 'imageAltText'))) { + const iconImage = document.createElement('img'); + iconImage.src = response?.imageUrl; + iconImage.alt = response?.imageAltText; + parentElement.appendChild(iconImage); + } + }) +} + async function buildProduct(product) { const productParent = document.createElement('div'); const productMapping = await getProductMapping(product); @@ -470,3 +477,8 @@ function sortColumn(dir, property) { container.appendChild(row); }); } + +function dateFormat(dateString) { + const formattedDate = dateString ? dateString.split('T')[0] : 'Not Available'; + return formattedDate; +} \ No newline at end of file diff --git a/scripts/graphql.js b/scripts/graphql.js index 72f6ceaa..9ed80bce 100644 --- a/scripts/graphql.js +++ b/scripts/graphql.js @@ -129,36 +129,6 @@ export async function graphqlCampaignByName(campaignName) { }); } -export async function graphqlFilterOnMarketingInitiative(marketingInitiative) { - - const baseApiUrl = `${await getGraphqlEndpoint()}/graphql/execute.json`; - const projectId = 'gmo'; - const queryName = 'filter-on-marketing-initiative'; - const encodedMarketingInitiative = encodeURIComponent(marketingInitiative); - const encodedSemiColon = encodeURIComponent(';'); - //persisted query URLs have to be encoded together with the first semicolon - const graphqlEndpoint = `${baseApiUrl}/${projectId}/${queryName}${encodedSemiColon}marketingInitiative=${encodedMarketingInitiative}`; - const jwtToken = await getBearerToken(); - - // Return the fetch promise chain so that it can be awaited outside - return fetch(graphqlEndpoint, { - method: 'GET', - headers: { - Authorization: jwtToken, - }, - }).then(response => { - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - return response.json(); - }).then(data => { - return data; // Make sure to return the data so that the promise resolves with it - }).catch(error => { - console.error('Error fetching data: ', error); - throw error; // Rethrow or handle error as appropriate - }); -} - async function getGraphqlEndpoint() { const result = await getAdminConfig(); return result.aemGraphqlEndpoint;