diff --git a/README.md b/README.md index f708647..1e13b25 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,91 @@ npm run test:unit npm run test:e2e ``` +## Architecture + +### DOM Structure and Element Cloning + +This extension displays English version update information by cloning the existing `article-metadata-footer` element on Microsoft Learn pages. + +#### HTML Structure + +The Microsoft Learn page has the following structure: + +```html +
` element containing the English version update information + - Apply appropriate styling based on whether the English version is newer + +#### Why This Approach? + +- **Consistency**: By cloning the existing metadata footer, we inherit all the proper CSS classes and structure +- **Maintainability**: If Microsoft changes the metadata structure, our extension adapts automatically +- **Visibility**: Placing the update information near the top of the page ensures users see it immediately +- **Clone-based**: We clone from `article-metadata-footer` at the bottom but display at the top for better UX + ## Contribution Contributions are welcome! Follow these steps to contribute: diff --git a/src/content.js b/src/content.js index 2866d6a..cd9a037 100644 --- a/src/content.js +++ b/src/content.js @@ -153,8 +153,47 @@ const applyStyles = (element, styles) => { const timeAgo = calculateTimeAgo(timeDifference, currentLang); const timeAgoStr = ` (${timeAgo})`; - const updateInfo = document.createElement("p"); - dataArticleDateElement.parentElement.appendChild(updateInfo); + // Clone article-metadata-footer and create custom-header-from-article-metadata-footer + let customContainer = document.getElementById('custom-header-from-article-metadata-footer'); + const articleMetadata = document.getElementById('article-metadata'); + const articleMetadataFooter = document.getElementById('article-metadata-footer'); + + let updateInfo; + if (!customContainer && articleMetadata && articleMetadataFooter) { + customContainer = articleMetadataFooter.cloneNode(true); + customContainer.id = 'custom-header-from-article-metadata-footer'; + customContainer.setAttribute('data-bi-name', 'custom-header-from-article-metadata-footer'); + customContainer.setAttribute('data-test-id', 'custom-header-from-article-metadata-footer'); + customContainer.className = 'custom-page-metadata-container'; + + // Update lang attribute + const ul = customContainer.querySelector('ul.metadata.page-metadata'); + if (ul) { + ul.setAttribute('lang', currentLang); + } + + // Add p tag after span.badge in li + const li = customContainer.querySelector('li.visibility-hidden-visual-diff'); + if (li) { + updateInfo = document.createElement('p'); + li.appendChild(updateInfo); + } + + articleMetadata.insertAdjacentElement('afterend', customContainer); + + // Add hr element below custom container + const hr = document.createElement('hr'); + hr.className = 'hr'; + customContainer.insertAdjacentElement('afterend', hr); + } else if (customContainer) { + updateInfo = customContainer.querySelector('li.visibility-hidden-visual-diff p'); + } + + // Fallback: if custom container doesn't exist, use original implementation + if (!updateInfo) { + updateInfo = document.createElement("p"); + dataArticleDateElement.parentElement.appendChild(updateInfo); + } const updateClass = () => { // if theme is selected, apply appropriate text color based on theme diff --git a/tests/unit/content.test.js b/tests/unit/content.test.js index 6536788..e689e6d 100644 --- a/tests/unit/content.test.js +++ b/tests/unit/content.test.js @@ -90,3 +90,58 @@ describe('URL Check in Content Script', () => { expect(match).toBeNull(); }); }); + +describe('DOM manipulation in content script', () => { + beforeEach(() => { + // Set up the DOM + document.body.innerHTML = ` +