From 1111dddf6adce428d8fb9f41003cbfe5e3f89830 Mon Sep 17 00:00:00 2001 From: koudaiii Date: Thu, 20 Nov 2025 11:30:29 +0900 Subject: [PATCH 1/3] Move updateInfo from footer to header Create custom-header-from-article-metadata-footer container to improve article metadata layout. --- src/content.js | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/content.js b/src/content.js index 2866d6a..98021ba 100644 --- a/src/content.js +++ b/src/content.js @@ -153,8 +153,42 @@ 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); + } 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 From b04a535c4555da2908c300b265696d17d89b7b77 Mon Sep 17 00:00:00 2001 From: koudaiii Date: Thu, 20 Nov 2025 12:03:37 +0900 Subject: [PATCH 2/3] Added `
` in the buttom. And add doc --- README.md | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/content.js | 5 +++ 2 files changed, 90 insertions(+) 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 +
+
+ +
...
+ + +

Title

+ + +
+
...
+
+ + +
+
+ +
+ +
+ + +
...
+ + + + +
+
+ +
+
+
+``` + +#### Cloning Strategy + +The extension uses the following approach: + +1. **Clone Source**: `article-metadata-footer` element (bottom of the page) + - This element contains the page's metadata structure with proper styling + +2. **Clone Process**: + ```javascript + customContainer = articleMetadataFooter.cloneNode(true); + customContainer.id = 'custom-header-from-article-metadata-footer'; + ``` + +3. **Insertion Point**: Inserted immediately after `article-metadata` (top of the page) + ```javascript + articleMetadata.insertAdjacentElement('afterend', customContainer); + ``` + +4. **Customization**: + - Update the `lang` attribute to match the current page language + - Add a new `

` 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 98021ba..cd9a037 100644 --- a/src/content.js +++ b/src/content.js @@ -180,6 +180,11 @@ const applyStyles = (element, styles) => { } 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'); } From 9daf73a9b22615f1261c70bec18d23f291eeec39 Mon Sep 17 00:00:00 2001 From: koudaiii Date: Wed, 26 Nov 2025 23:37:18 +0900 Subject: [PATCH 3/3] test: Add test for custom-header --- tests/unit/content.test.js | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) 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 = ` +

+ +
+
+ +
+ + `; + + // Mock window.location.href + Object.defineProperty(window, 'location', { + value: { + href: 'https://learn.microsoft.com/ja-jp/test', + }, + writable: true + }); + + // Mock fetch + global.fetch = jest.fn(() => + Promise.resolve({ + text: () => Promise.resolve(''), + }) + ); + }); + + test('should create and insert the custom header', async () => { + // Run the content script + require('../../src/content.js'); + + // Wait for the async operations to complete + await new Promise(resolve => setTimeout(resolve, 100)); + + // Check if the custom header was created + const customHeader = document.getElementById('custom-header-from-article-metadata-footer'); + expect(customHeader).not.toBeNull(); + + // Check if the custom header is in the correct position + const articleMetadata = document.getElementById('article-metadata'); + expect(articleMetadata.nextElementSibling).toBe(customHeader); + + // Check if the update information is correct + const updateInfo = customHeader.querySelector('p'); + expect(updateInfo).not.toBeNull(); + expect(updateInfo.innerHTML).toContain('英語版の更新日'); + }); +}); \ No newline at end of file