From 2d7a325862f26bcc756a31ab3dae26bf6f2aed4f Mon Sep 17 00:00:00 2001 From: st-angelo-adobe Date: Wed, 11 Sep 2024 17:24:25 +0300 Subject: [PATCH 1/2] MWPW-158176: Production URL and cleanup --- acrobat/blocks/rnr/rnr.js | 37 +++++++++++++++----------------- test/blocks/rnr/rnr.test.js | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/acrobat/blocks/rnr/rnr.js b/acrobat/blocks/rnr/rnr.js index aae2a6f6..63356284 100644 --- a/acrobat/blocks/rnr/rnr.js +++ b/acrobat/blocks/rnr/rnr.js @@ -10,8 +10,15 @@ const { createTag } = await import(`${miloLibs}/utils/utils.js`); const COMMENTS_MAX_LENGTH = 500; const SHOW_COMMENTS_TRESHOLD = 5; -const RNR_API_URL = 'https://rnr-stage.adobe.io/v1'; const ASSET_TYPE = 'ADOBE_COM'; +const RNR_API_URL = (function () { + if ( + window.location.origin === 'main--dc--adobecom.hlx.page' + || window.location.origin === 'main--dc--adobecom.hlx.live' + || window.location.origin === 'www.adobe.com' + ) return 'https://rnr-prod.adobe.io/v1'; + return 'https://rnr-stage.adobe.io/v1'; +}()); // #endregion @@ -40,7 +47,7 @@ function createSnapshot(rating, currentAverage, currentVotes) { // #region Extract metadata from options -const metadata = JSON.parse('{"labels":{}}'); +const metadata = JSON.parse('{}'); const getOptions = (el) => { const keyDivs = el.querySelectorAll(':scope > div > div:first-child'); @@ -221,12 +228,12 @@ function initRatingFielset(fieldset, rnrForm, showComments) { }; let commentsShown = false; - const selectRating = (value, triggeredByKeyboard = false) => { + const selectRating = (value) => { const rating = parseInt(value, 10); selectedRating = rating; if (commentsShown) return; if (rating <= metadata.showCommentsThreshold) { - showComments(triggeredByKeyboard); + showComments(); commentsShown = true; } else { // form.submit() will not trigger the even handler @@ -318,7 +325,6 @@ function initCommentsFieldset(fieldset) { cols: 40, maxLength: metadata.commentsMaxLength, placeholder: window.mph['rnr-comments-placeholder'], - readonly: 'readonly', }); if (!metadata.interactive) textarea.setAttribute('disabled', 'disabled'); @@ -350,18 +356,6 @@ function initCommentsFieldset(fieldset) { else submitTag.setAttribute('disabled', 'disabled'); }); - /* This is needed because when the comments area is shown after a rating selection by - * keyboard (Enter) that 'Enter' keypress still counts as input for the newly focused - * textarea. To prevent this, the textarea is readonly by default, and 'readonly' is - * removed after the first keypress, or when the selection was done by mouse (see below) - */ - function onTextareaKeyup(ev) { - if (ev.code !== 'Enter') return; - textarea.removeAttribute('readonly'); - textarea.removeEventListener('keyup', onTextareaKeyup); - } - textarea.addEventListener('keyup', onTextareaKeyup); - fieldset.append(textarea, footerContainer); } @@ -372,7 +366,11 @@ function initSummary(container) { const voteLabels = (window.mph['rnr-rating-verb'] || ',').split(',').map((verb) => verb.trim()); const votesLabel = votes === 1 ? voteLabels[0] : voteLabels[1]; - const averageTag = createTag('span', { class: 'rnr-summary-average' }, average.toFixed(1)); + const averageTag = createTag( + 'span', + { class: 'rnr-summary-average' }, + average.toFixed(1).replace('.0', ''), + ); const scoreSeparator = createTag('span', {}, '/'); const outOfTag = createTag('span', { class: 'rnr-summary-outOf' }, outOf); const votesSeparator = createTag('span', {}, '-'); @@ -420,11 +418,10 @@ function initControls(element) { form.addEventListener('submit', submit); // Show comments - const showComments = (triggeredByKeyboard) => { + const showComments = () => { form.insertBefore(commentsFieldset, null); const textarea = commentsFieldset.querySelector('textarea'); textarea.focus(); - if (!triggeredByKeyboard) textarea.removeAttribute('readonly'); }; // Init rating diff --git a/test/blocks/rnr/rnr.test.js b/test/blocks/rnr/rnr.test.js index 07dd77ee..9385b73b 100644 --- a/test/blocks/rnr/rnr.test.js +++ b/test/blocks/rnr/rnr.test.js @@ -332,5 +332,47 @@ describe('rnr - Ratings and reviews', () => { window.fetch.restore(); }); + it('should render round average', async () => { + sinon.stub(window, 'fetch'); + window.fetch.returns( + Promise.resolve({ + json: () => Promise.resolve({ + overallRating: 2.666666666666667, + ratingHistogram: { rating1: 0, rating2: 5, rating3: 10, rating4: 0, rating5: 0 }, + }), + ok: true, + }), + ); + document.body.innerHTML = await readFile({ path: './mocks/body.html' }); + const rnr = document.querySelector('.rnr'); + await init(rnr); + const containerElement = await waitForElement('.rnr-container'); + expect(containerElement).to.exist; + const averageElement = containerElement.querySelector('.rnr-summary-average'); + expect(averageElement.textContent).to.equal('2.7'); + window.fetch.restore(); + }); + + it('should render should display integer averages without decimals', async () => { + sinon.stub(window, 'fetch'); + window.fetch.returns( + Promise.resolve({ + json: () => Promise.resolve({ + overallRating: 2, + ratingHistogram: { rating1: 0, rating2: 2, rating3: 0, rating4: 0, rating5: 0 }, + }), + ok: true, + }), + ); + document.body.innerHTML = await readFile({ path: './mocks/body.html' }); + const rnr = document.querySelector('.rnr'); + await init(rnr); + const containerElement = await waitForElement('.rnr-container'); + expect(containerElement).to.exist; + const averageElement = containerElement.querySelector('.rnr-summary-average'); + expect(averageElement.textContent).to.equal('2'); + window.fetch.restore(); + }); + // #endregion }); From f8063d9715554924320ffa2492e025c70e618171 Mon Sep 17 00:00:00 2001 From: st-angelo-adobe Date: Mon, 16 Sep 2024 12:34:43 +0300 Subject: [PATCH 2/2] Api call changes and added locale metadata --- acrobat/blocks/rnr/rnr.js | 115 ++++++++++++++++-------------- acrobat/scripts/maps/localeMap.js | 96 ++++++++++++------------- 2 files changed, 111 insertions(+), 100 deletions(-) diff --git a/acrobat/blocks/rnr/rnr.js b/acrobat/blocks/rnr/rnr.js index 63356284..b7aeb03f 100644 --- a/acrobat/blocks/rnr/rnr.js +++ b/acrobat/blocks/rnr/rnr.js @@ -1,6 +1,7 @@ /* eslint-disable func-names */ /* eslint-disable compat/compat */ /* eslint-disable max-len */ +import localeMap from '../../scripts/maps/localeMap.js'; import { setLibs } from '../../scripts/utils.js'; const miloLibs = setLibs('/libs'); @@ -122,62 +123,72 @@ function setJsonLdProductInfo() { // #endregion -function loadRnrData() { - const headers = { - Accept: 'application/vnd.adobe-review.review-overall-rating-v1+json', - 'x-api-key': 'ffc-addon-service', - Authorization: window.adobeIMS.getAccessToken()?.token, - }; - - return fetch(`${RNR_API_URL}/ratings?assetType=${ASSET_TYPE}&assetId=${metadata.verb}`, { headers }) - .then(async (response) => { - if (!response.ok) { - const res = await response.json(); - throw new Error(res.message); - } - return response.json(); - }) - .then((result) => { - if (!result) throw new Error(`Received empty ratings data for asset '${metadata.verb}'.`); - const { overallRating, ratingHistogram } = result; - if (!overallRating || !ratingHistogram) throw new Error(`Missing aggregated rating data in response for asset '${metadata.verb}'.`); - rnrData.average = overallRating; - rnrData.votes = Object.keys(ratingHistogram).reduce( - (total, key) => total + ratingHistogram[key], - 0, - ); - setJsonLdProductInfo(); - }) - .catch((error) => { - window.lana?.log(`Could not load review data: ${error?.message}`); - }); -} +async function loadRnrData() { + try { + const headers = { + Accept: 'application/vnd.adobe-review.review-overall-rating-v1+json', + 'x-api-key': 'ffc-addon-service', + Authorization: window.adobeIMS.getAccessToken()?.token, + }; -function postReview(data) { - const body = JSON.stringify({ - assetType: ASSET_TYPE, - assetId: metadata.verb, - rating: data.rating, - text: data.comments, - authorName: 'guest', - assetMetadata: { version: 1.1 }, - }); - const headers = { - Accept: 'application/vnd.adobe-review.review-data-v1+json', - 'Content-Type': 'application/vnd.adobe-review.review-request-v1+json', - 'x-api-key': 'rnr-client', - Authorization: window.adobeIMS.getAccessToken()?.token, - }; + const response = await fetch( + `${RNR_API_URL}/ratings?assetType=${ASSET_TYPE}&assetId=${metadata.verb}`, + { headers }, + ); - fetch(`${RNR_API_URL}/reviews`, { method: 'POST', body, headers }) - .then(async (result) => { - if (result.ok) return; - const res = await result.json(); + if (!response.ok) { + const res = await response.json(); throw new Error(res.message); - }) - .catch((error) => { - window.lana?.log(`Could not post review: ${error?.message}`); + } + + const result = await response.json(); + if (!result) throw new Error(`Received empty ratings data for asset '${metadata.verb}'.`); + const { overallRating, ratingHistogram } = result; + if (!overallRating || !ratingHistogram) { + throw new Error(`Missing aggregated rating data in response for asset '${metadata.verb}'.`); + } + rnrData.average = overallRating; + rnrData.votes = Object.keys(ratingHistogram).reduce( + (total, key) => total + ratingHistogram[key], + 0, + ); + + setJsonLdProductInfo(); + } catch (error) { + window.lana?.log(`Could not load review data: ${error?.message}`); + } +} + +async function postReview(data) { + try { + // Get locale + const languageFromPath = window.location.pathname.split('/')[1]; + const locale = localeMap[languageFromPath] || 'en-us'; + + const body = JSON.stringify({ + assetType: ASSET_TYPE, + assetId: metadata.verb, + rating: data.rating, + text: data.comments, + authorName: window.adobeIMS.getUserProfile?.call()?.name || 'Anonymous', + assetMetadata: { locale }, }); + const headers = { + Accept: 'application/vnd.adobe-review.review-data-v1+json', + 'Content-Type': 'application/vnd.adobe-review.review-request-v1+json', + 'x-api-key': 'rnr-client', + Authorization: window.adobeIMS.getAccessToken()?.token, + }; + + const response = await fetch(`${RNR_API_URL}/reviews`, { method: 'POST', body, headers }); + + if (response.ok) return; + + const res = await response.json(); + throw new Error(res.message); + } catch (error) { + window.lana?.log(`Could not post review: ${error?.message}`); + } } // #endregion diff --git a/acrobat/scripts/maps/localeMap.js b/acrobat/scripts/maps/localeMap.js index 67512d16..265dc4bb 100644 --- a/acrobat/scripts/maps/localeMap.js +++ b/acrobat/scripts/maps/localeMap.js @@ -1,51 +1,51 @@ const localeMap = { - 'ca_fr': 'fr-FR', - 'be_fr': 'fr-FR', - 'dk': 'da-DK', - 'de': 'de-DE', - 'lu_de': 'de-DE', - 'ch_de': 'de-DE', - 'at': 'de-DE', - 'es': 'es-ES', - 'ar': 'es-ES', - 'cl': 'es-ES', - 'co': 'es-ES', - 'cr': 'es-ES', - 'ec': 'es-ES', - 'gt': 'es-ES', - 'pe': 'es-ES', - 'pr': 'es-ES', - 'fi': 'fi-FI', - 'fr': 'fr-FR', - 'ch_fr': 'fr-FR', - 'lu_fr': 'fr-FR', - 'it': 'it-IT', - 'ch_it': 'it-IT', - 'jp': 'ja-JP', - 'nb': 'nb-NO', - 'no': 'nb-NO', - 'nl': 'nl-NL', - 'pt': 'pt-BR', - 'sv': 'sv-SE', - 'se': 'sv-SE', - 'zh_cn': 'zh-CN', - 'zh_hk': 'zh-TW', - 'hk_zh': 'zh-hant-hk', - 'tw': 'zh-hant-tw', - 'kr': 'ko-KR', - 'cz': 'cs-CZ', - 'pl': 'pl-PL', - 'ru': 'ru-RU', - 'tr': 'tr-TR', - 'br': 'pt-BR', - 'la': 'es-ES', - 'mx': 'es-ES', - 'be_nl': 'nl-NL', - 'bg': 'bg-BG', - 'ee': 'et-EE', - 'lt': 'lt-LT', - 'lv': 'lv-LV', - 'ua': 'uk-UA', - 'si': 'sl-SI' + ca_fr: 'fr-FR', + be_fr: 'fr-FR', + dk: 'da-DK', + de: 'de-DE', + lu_de: 'de-DE', + ch_de: 'de-DE', + at: 'de-DE', + es: 'es-ES', + ar: 'es-ES', + cl: 'es-ES', + co: 'es-ES', + cr: 'es-ES', + ec: 'es-ES', + gt: 'es-ES', + pe: 'es-ES', + pr: 'es-ES', + fi: 'fi-FI', + fr: 'fr-FR', + ch_fr: 'fr-FR', + lu_fr: 'fr-FR', + it: 'it-IT', + ch_it: 'it-IT', + jp: 'ja-JP', + nb: 'nb-NO', + no: 'nb-NO', + nl: 'nl-NL', + pt: 'pt-BR', + sv: 'sv-SE', + se: 'sv-SE', + zh_cn: 'zh-CN', + zh_hk: 'zh-TW', + hk_zh: 'zh-hant-hk', + tw: 'zh-hant-tw', + kr: 'ko-KR', + cz: 'cs-CZ', + pl: 'pl-PL', + ru: 'ru-RU', + tr: 'tr-TR', + br: 'pt-BR', + la: 'es-ES', + mx: 'es-ES', + be_nl: 'nl-NL', + bg: 'bg-BG', + ee: 'et-EE', + lt: 'lt-LT', + lv: 'lv-LV', + ua: 'uk-UA', + si: 'sl-SI', }; export default localeMap;