diff --git a/boulder_base.libraries.yml b/boulder_base.libraries.yml index 9f2f5c23..c473b0f0 100644 --- a/boulder_base.libraries.yml +++ b/boulder_base.libraries.yml @@ -305,14 +305,14 @@ ucb-article-grid-block: js/ucb-article-grid-block.js: {} css: theme: - css/ucb-article-grid-block.css: {} + css/block/ucb-article-grid-block.css: {} ucb-article-list-block: version: 1.x js: js/ucb-article-list-block.js: {} css: theme: - css/ucb-article-list-block.css: {} + css/block/ucb-article-list-block.css: {} ucb-content-sequence: version: 1.x js: diff --git a/css/block/ucb-article-feature-block.css b/css/block/ucb-article-feature-block.css index 8a89e23d..d5d4aa9d 100644 --- a/css/block/ucb-article-feature-block.css +++ b/css/block/ucb-article-feature-block.css @@ -48,16 +48,16 @@ .ucb-article-card{ margin-bottom: 10px; - border-bottom: solid rgba(128, 128, 128, 0.333); + border-bottom: solid 1px rgba(128, 128, 128, 0.333); padding-bottom: 10px; } .ucb-stacked-secondary-container{ margin-top: 10px; padding-top: 20px; - border-top: solid rgba(128, 128, 128, 0.333); + border-top: solid 1px rgba(128, 128, 128, 0.333); margin-bottom: 10px; - border-bottom: solid rgba(128, 128, 128, 0.333); + border-bottom: solid 1px rgba(128, 128, 128, 0.333); padding-bottom: 10px; margin-left: 10px; margin-right: 30px; @@ -100,12 +100,12 @@ @media screen and (max-width: 767px) { .ucb-stacked-secondary-container > .ucb-article-card{ margin-bottom: 10px; - border-bottom: solid rgba(128, 128, 128, 0.333); + border-bottom: solid 1px rgba(128, 128, 128, 0.333); padding-bottom: 10px; padding-top: 10px; } .ucb-stacked-secondary-container > .ucb-article-card:first-child { - border-top: solid rgba(128, 128, 128, 0.333); + border-top: solid 1px rgba(128, 128, 128, 0.333); padding-top: 10px; } diff --git a/css/ucb-article-grid-block.css b/css/block/ucb-article-grid-block.css similarity index 100% rename from css/ucb-article-grid-block.css rename to css/block/ucb-article-grid-block.css diff --git a/css/ucb-article-list-block.css b/css/block/ucb-article-list-block.css similarity index 61% rename from css/ucb-article-list-block.css rename to css/block/ucb-article-list-block.css index 0027e527..a02fe0e2 100644 --- a/css/ucb-article-list-block.css +++ b/css/block/ucb-article-list-block.css @@ -1,29 +1,21 @@ .ucb-article-list-block-button-container{ - text-align: center; + text-align: right; } .ucb-article-list-block-button{ - display: inline-block; - padding: 5px 10px; - font-weight: bold; - font-family: "Roboto","Helvetica Neue",Helvetica,Arial,sans-serif; - margin-bottom: 5px; - background-clip: padding-box; - color: #0277BD !important; - border: 1px solid #0277BD; - border-radius: 0px; - background-color: transparent; + font-weight: normal; + margin: 0; } -.ucb-article-list-block-button:hover{ +/* .ucb-article-list-block-button:hover{ transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease; background-color: #0277BD; color: white !important; -} +} */ .ucb-article-card{ margin-bottom: 20px; - border-bottom: solid rgba(128, 128, 128, 0.333); + border-bottom: solid 1px rgba(128, 128, 128, 0.333); padding-bottom: 20px; } @@ -32,15 +24,22 @@ } .ucb-article-card-img{ - width: 110px; - height: 110px; - padding: 0 10px 10px 0; + width: 100px; + height: 100px; + padding: 0px; +} + +.ucb-article-card-img.title-thumbnail-img{ + width: 65px; + height: 65px; + padding: 0px; } .ucb-article-card-img-wide{ height: 500px !important; object-fit: cover !important; margin-bottom: 20px; + width: 100%; } .ucb-article-card-img-full{ @@ -71,4 +70,18 @@ font-weight: normal; margin: 0; line-height: 1.3; -} \ No newline at end of file +} + +@media only screen and (max-width: 600px) { + .ucb-article-card-img{ + width: 50px; + height: 50px; + padding: 0px; + } + + .ucb-article-card-img.title-thumbnail-img{ + width: 50px; + height: 50px; + padding: 0px; + } + } \ No newline at end of file diff --git a/js/ucb-article-feature-block.js b/js/ucb-article-feature-block.js index d8173407..99e35a2f 100644 --- a/js/ucb-article-feature-block.js +++ b/js/ucb-article-feature-block.js @@ -22,7 +22,7 @@ class ArticleFeatureBlockElement extends HTMLElement { } // This is the article list filtering function that assembles the article javascript object array from the JSON response. Handles exclusions of categories and tags. - build(data, display, count, imgSize, excludeCatArray, excludeTagArray, finalArticles = []){ + async build(data, display, count, imgSize, excludeCatArray, excludeTagArray, finalArticles = []){ // More than 10 articles? This sets up the next call if there's more articles to be retrieved but not enough post-filters let NEXTJSONURL = ""; if(data.links.next) { @@ -70,7 +70,7 @@ class ArticleFeatureBlockElement extends HTMLElement { }) } // Iterate over each Article - data.data.map(item=>{ + await Promise.all (data.data.map(async(item)=>{ // Article must have a thumbnail if(item.relationships.field_ucb_article_thumbnail.data){ let thisArticleCats = []; @@ -118,33 +118,7 @@ class ArticleFeatureBlockElement extends HTMLElement { let imageSrcWide = ""; if (!body.length && bodyAndImageId != "") { - this.getArticleParagraph(bodyAndImageId) - .then((response) => response.json()) - .then((data) => { - // Remove any html tags within the article - let htmlStrip = data.data.attributes.field_article_text.processed.replace( - /<\/?[^>]+(>|$)/g, - "" - ) - // Remove any line breaks if media is embedded in the body - let lineBreakStrip = htmlStrip.replace(/(\r\n|\n|\r)/gm, ""); - // take only the first 100 words ~ 500 chars - let trimmedString = lineBreakStrip.substr(0, 250); - // if in the middle of the string, take the whole word - if(trimmedString.length > 100){ - trimmedString = trimmedString.substr( - 0, - Math.min( - trimmedString.length, - trimmedString.lastIndexOf(" ") - ) - ) - body = `${trimmedString}...`; - } - // set the contentBody of Article Summary card to the minified body instead - body = `${trimmedString}`; - document.getElementById(`body-${bodyAndImageId}`).innerText = body; - }) + body = await this.getArticleParagraph(bodyAndImageId); } //Use the idObj as a memo to add the corresponding image url let thumbId = item.relationships.field_ucb_article_thumbnail.data.id; @@ -170,7 +144,7 @@ class ArticleFeatureBlockElement extends HTMLElement { finalArticles.push(article) } } - }) + })); // Case for not enough articles selected and extra articles available if(finalArticles.length < count && NEXTJSONURL){ fetch(NEXTJSONURL).then(this.handleError) @@ -201,17 +175,38 @@ class ArticleFeatureBlockElement extends HTMLElement { } } } - // Gets the body if no summary - async getArticleParagraph(id) { - if(id) { - const response = await fetch( - `/jsonapi/paragraph/article_content/${id}` - ); - return response; - } else { - return ""; - } - } + // Responsible for fetching & processing the body of the Article if no summary provided + async getArticleParagraph(id) { + if (!id) { + return ""; + } + + const response = await fetch(`/jsonapi/paragraph/article_content/${id}`); + if (!response.ok) { + throw new Error('Failed to fetch article paragraph'); + } + + const data = await response.json(); + + let htmlStrip = data.data.attributes.field_article_text.processed.replace( + /<\/?[^>]+(>|$)/g, + "" + ); + let lineBreakStrip = htmlStrip.replace(/(\r\n|\n|\r)/gm, ""); + let trimmedString = lineBreakStrip.substr(0, 250); + + if (trimmedString.length > 100) { + trimmedString = trimmedString.substr( + 0, + Math.min( + trimmedString.length, + trimmedString.lastIndexOf(" ") + ) + ); + } + + return trimmedString + "..."; + } // Responsible for calling the render function of the appropriate display renderDisplay(articleArray, display, imgSize){ diff --git a/js/ucb-article-grid-block.js b/js/ucb-article-grid-block.js index 4fd3d203..64fdd688 100644 --- a/js/ucb-article-grid-block.js +++ b/js/ucb-article-grid-block.js @@ -20,7 +20,7 @@ class ArticleGridBlockElement extends HTMLElement { }); } - build(data, count, includeSummary, excludeCatArray, excludeTagArray, finalArticles = []){ + async build(data, count, includeSummary, excludeCatArray, excludeTagArray, finalArticles = []){ // More than 10 articles? This sets up the next call if there's more articles to be retrieved but not enough post-filters let NEXTJSONURL = ""; if(data.links.next) { @@ -65,7 +65,7 @@ class ArticleGridBlockElement extends HTMLElement { }) } // Iterate over each Article - data.data.map(item=>{ + await Promise.all(data.data.map(async (item)=>{ // Article must have a thumbnail if(item.relationships.field_ucb_article_thumbnail.data){ let thisArticleCats = []; @@ -112,33 +112,7 @@ class ArticleGridBlockElement extends HTMLElement { let imageSrc = ""; if (!body.length && bodyAndImageId != "") { - this.getArticleParagraph(bodyAndImageId) - .then((response) => response.json()) - .then((data) => { - // Remove any html tags within the article - let htmlStrip = data.data.attributes.field_article_text.processed.replace( - /<\/?[^>]+(>|$)/g, - "" - ) - // Remove any line breaks if media is embedded in the body - let lineBreakStrip = htmlStrip.replace(/(\r\n|\n|\r)/gm, ""); - // take only the first 100 words ~ 500 chars - let trimmedString = lineBreakStrip.substr(0, 250); - // if in the middle of the string, take the whole word - if(trimmedString.length > 100){ - trimmedString = trimmedString.substr( - 0, - Math.min( - trimmedString.length, - trimmedString.lastIndexOf(" ") - ) - ) - body = `${trimmedString}...`; - } - // set the contentBody of Article Summary card to the minified body instead - body = `${trimmedString}`; - document.getElementById(`body-${bodyAndImageId}`).innerText = body; - }) + body = await this.getArticleParagraph(bodyAndImageId); } // if no thumbnail, show no image @@ -168,7 +142,7 @@ class ArticleGridBlockElement extends HTMLElement { finalArticles.push(article) } } - }) + })); // Case for not enough articles selected and extra articles available if(finalArticles.length < count && NEXTJSONURL){ fetch(NEXTJSONURL).then(this.handleError) @@ -200,15 +174,37 @@ class ArticleGridBlockElement extends HTMLElement { } } + // Responsible for fetching & processing the body of the Article if no summary provided async getArticleParagraph(id) { - if(id) { - const response = await fetch( - `/jsonapi/paragraph/article_content/${id}` + if (!id) { + return ""; + } + + const response = await fetch(`/jsonapi/paragraph/article_content/${id}`); + if (!response.ok) { + throw new Error('Failed to fetch article paragraph'); + } + + const data = await response.json(); + + let htmlStrip = data.data.attributes.field_article_text.processed.replace( + /<\/?[^>]+(>|$)/g, + "" ); - return response; - } else { - return ""; - } + let lineBreakStrip = htmlStrip.replace(/(\r\n|\n|\r)/gm, ""); + let trimmedString = lineBreakStrip.substr(0, 250); + + if (trimmedString.length > 100) { + trimmedString = trimmedString.substr( + 0, + Math.min( + trimmedString.length, + trimmedString.lastIndexOf(" ") + ) + ); + } + + return trimmedString + "..."; } // Responsible for calling the render function of the appropriate display diff --git a/js/ucb-article-list-block.js b/js/ucb-article-list-block.js index cb2507e8..3bb62edf 100644 --- a/js/ucb-article-list-block.js +++ b/js/ucb-article-list-block.js @@ -20,7 +20,7 @@ class ArticleListBlockElement extends HTMLElement { }); } - build(data, count, display, excludeCatArray, excludeTagArray, finalArticles = []){ + async build(data, count, display, excludeCatArray, excludeTagArray, finalArticles = []){ // More than 10 articles? This sets up the next call if there's more articles to be retrieved but not enough post-filters let NEXTJSONURL = ""; if(data.links.next) { @@ -39,6 +39,7 @@ class ArticleListBlockElement extends HTMLElement { // Below objects are needed to match images with their corresponding articles. There are two endpoints => data.data (article) and data.included (incl. media), both needed to associate a media library image with its respective article let idObj = {}; let altObj = {}; + let wideObj = {}; // Remove any blanks from our articles before map if (data.included) { // removes all other included data besides images in our included media @@ -54,6 +55,8 @@ class ArticleListBlockElement extends HTMLElement { // checks if consumer is working, else default to standard image instead of focal image if(item.links.focal_image_square != undefined){ altObj[item.id] = item.links.focal_image_square.href + // This is used if a user selects the "Wide" image style + wideObj[item.id] = item.links.focal_image_wide.href } else { altObj[item.id] = item.attributes.uri.url } @@ -66,8 +69,8 @@ class ArticleListBlockElement extends HTMLElement { } // Iterate over each Article - data.data.map(item=>{ - let thisArticleCats = []; + await Promise.all(data.data.map(async (item) => { + let thisArticleCats = []; let thisArticleTags = []; // // loop through and grab all of the categories if (item.relationships.field_ucb_article_categories) { @@ -108,36 +111,11 @@ class ArticleListBlockElement extends HTMLElement { let body = item.attributes.field_ucb_article_summary ? item.attributes.field_ucb_article_summary : ""; body = body.trim(); let imageSrc = ""; + let imageSrcWide = ""; if (!body.length && bodyAndImageId != "") { - this.getArticleParagraph(bodyAndImageId) - .then((response) => response.json()) - .then((data) => { - // Remove any html tags within the article - let htmlStrip = data.data.attributes.field_article_text.processed.replace( - /<\/?[^>]+(>|$)/g, - "" - ) - // Remove any line breaks if media is embedded in the body - let lineBreakStrip = htmlStrip.replace(/(\r\n|\n|\r)/gm, ""); - // take only the first 100 words ~ 500 chars - let trimmedString = lineBreakStrip.substr(0, 250); - // if in the middle of the string, take the whole word - if(trimmedString.length > 100){ - trimmedString = trimmedString.substr( - 0, - Math.min( - trimmedString.length, - trimmedString.lastIndexOf(" ") - ) - ) - body = `${trimmedString}...`; - } - // set the contentBody of Article Summary card to the minified body instead - body = `${trimmedString}`; - document.getElementById(`body-${bodyAndImageId}`).innerText = body; - }) - } + body = await this.getArticleParagraph(bodyAndImageId); + } // if no thumbnail, show no image if (!item.relationships.field_ucb_article_thumbnail.data) { @@ -146,6 +124,7 @@ class ArticleListBlockElement extends HTMLElement { //Use the idObj as a memo to add the corresponding image url let thumbId = item.relationships.field_ucb_article_thumbnail.data.id; imageSrc = altObj[idObj[thumbId]]; + imageSrcWide = wideObj[idObj[thumbId]] } //Date - make human readable @@ -159,13 +138,14 @@ class ArticleListBlockElement extends HTMLElement { title, link, image: imageSrc, + imageWide: imageSrcWide, date, body, } // Adds the article object to the final array of articles chosen finalArticles.push(article) } - }) + })); // Case for not enough articles selected and extra articles available if(finalArticles.length < count && NEXTJSONURL){ fetch(NEXTJSONURL).then(this.handleError) @@ -196,47 +176,69 @@ class ArticleListBlockElement extends HTMLElement { } } } - + // Responsible for fetching & processing the body of the Article if no summary provided async getArticleParagraph(id) { - if(id) { - const response = await fetch( - `/jsonapi/paragraph/article_content/${id}` + if (!id) { + return ""; + } + + const response = await fetch(`/jsonapi/paragraph/article_content/${id}`); + if (!response.ok) { + throw new Error('Failed to fetch article paragraph'); + } + + const data = await response.json(); + + let htmlStrip = data.data.attributes.field_article_text.processed.replace( + /<\/?[^>]+(>|$)/g, + "" + ); + let lineBreakStrip = htmlStrip.replace(/(\r\n|\n|\r)/gm, ""); + let trimmedString = lineBreakStrip.substr(0, 250); + + if (trimmedString.length > 100) { + trimmedString = trimmedString.substr( + 0, + Math.min( + trimmedString.length, + trimmedString.lastIndexOf(" ") + ) ); - return response; - } else { - return ""; - } + } + + return trimmedString + "..."; } + // Responsible for calling the render function of the appropriate display renderDisplay(display, articleArray){ - switch (display) { - case 'feature-wide': - this.renderFeatureWide(articleArray) - break; - case 'feature-large': - this.renderFeatureFull(articleArray) - break; - case 'teaser': - this.renderTeaser(articleArray) - break; - case 'title-thumbnail': - this.renderTitleThumb(articleArray) - break; - case 'title-only': - this.renderTitleOnly(articleArray) - break; - default: - this.renderTeaser(articleArray) - break; - } + switch (display) { + case 'feature-wide': + this.renderFeatureWide(articleArray) + break; + case 'feature-large': + this.renderFeatureFull(articleArray) + break; + case 'teaser': + this.renderTeaser(articleArray) + break; + case 'title-thumbnail': + this.renderTitleThumb(articleArray) + break; + case 'title-only': + this.renderTitleOnly(articleArray) + break; + default: + this.renderTeaser(articleArray) + break; + } } // renderX functions are responsible for taking the final array of Articles and displaying them appropriately renderFeatureFull(articleArray){ var container = document.createElement('div') - container.classList = 'ucb-article-list-block container' + container.classList = 'ucb-article-list-block' articleArray.forEach(article=>{ // Article Data @@ -248,7 +250,7 @@ class ArticleListBlockElement extends HTMLElement { // Create and Append Elements var article = document.createElement('article'); - article.classList = 'ucb-article-card row'; + article.classList = 'ucb-article-card'; if(articleImgSrc){ @@ -257,7 +259,7 @@ class ArticleListBlockElement extends HTMLElement { imgLink.href = articleLink; var articleImg = document.createElement('img') - articleImg.classList = 'ucb-article-card-img-full' + articleImg.classList = 'col-sm-12 ucb-article-card-img-full' articleImg.src = articleImgSrc; imgLink.appendChild(articleImg); @@ -267,7 +269,7 @@ class ArticleListBlockElement extends HTMLElement { } var articleBody = document.createElement('div'); - articleBody.classList = 'col-sm-12 col-md-10 ucb-article-card-data'; + articleBody.classList = 'col-sm-12 ucb-article-card-data'; var headerLink = document.createElement('a'); headerLink.href = articleLink; @@ -305,19 +307,19 @@ class ArticleListBlockElement extends HTMLElement { } renderFeatureWide(articleArray){ var container = document.createElement('div') - container.classList = 'ucb-article-list-block container' + container.classList = 'ucb-article-list-block' articleArray.forEach(article=>{ // Article Data var articleDate = article.date; var articleLink = article.link; - var articleImgSrc = article.image; + var articleImgSrc = article.imageWide; var articleTitle = article.title; var articleSumm = article.body; // Create and Append Elements var article = document.createElement('article'); - article.classList = 'ucb-article-card row'; + article.classList = 'ucb-article-card'; if(articleImgSrc){ var imgDiv = document.createElement('div'); @@ -335,7 +337,7 @@ class ArticleListBlockElement extends HTMLElement { } var articleBody = document.createElement('div'); - articleBody.classList = 'col-sm-12 col-md-10 ucb-article-card-data'; + articleBody.classList = 'col-sm-12 ucb-article-card-data'; var headerLink = document.createElement('a'); headerLink.href = articleLink; @@ -385,7 +387,7 @@ class ArticleListBlockElement extends HTMLElement { var article = document.createElement('article'); article.classList = 'ucb-article-card row'; var imgDiv = document.createElement('div'); - imgDiv.classList = 'col-sm-12 col-md-2 ucb-article-card-img'; + imgDiv.classList = 'ucb-article-card-img title-thumbnail-img'; var imgLink = document.createElement('a'); imgLink.href = articleLink; @@ -399,7 +401,7 @@ class ArticleListBlockElement extends HTMLElement { article.appendChild(imgDiv); var articleBody = document.createElement('div'); - articleBody.classList = 'col-sm-12 col-md-10 ucb-article-card-data'; + articleBody.classList = 'col px-3 ucb-article-card-data'; var headerLink = document.createElement('a'); headerLink.href = articleLink; @@ -469,7 +471,7 @@ class ArticleListBlockElement extends HTMLElement { var article = document.createElement('article'); article.classList = 'ucb-article-card row'; var imgDiv = document.createElement('div'); - imgDiv.classList = 'col-sm-12 col-md-2 ucb-article-card-img'; + imgDiv.classList = 'ucb-article-card-img'; var imgLink = document.createElement('a'); imgLink.href = articleLink; @@ -483,7 +485,7 @@ class ArticleListBlockElement extends HTMLElement { article.appendChild(imgDiv); var articleBody = document.createElement('div'); - articleBody.classList = 'col-sm-12 col-md-10 ucb-article-card-data'; + articleBody.classList = 'col px-3 ucb-article-card-data'; var headerLink = document.createElement('a'); headerLink.href = articleLink;