From 3693aaf0c417493dda67ab0bce10c4abaeaf2800 Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Tue, 17 Jun 2025 17:46:15 -0500 Subject: [PATCH 01/79] feat: WC-219 isolate external styles for component --- .../components/DataFiles/DataFilesTest.jsx | 27 +++++++ client/src/components/DataFiles/index.js | 2 +- client/src/hooks/datafiles/index.js | 1 + .../src/hooks/datafiles/useExternalStyles.jsx | 71 +++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 client/src/components/DataFiles/DataFilesTest.jsx create mode 100644 client/src/hooks/datafiles/useExternalStyles.jsx diff --git a/client/src/components/DataFiles/DataFilesTest.jsx b/client/src/components/DataFiles/DataFilesTest.jsx new file mode 100644 index 0000000000..419452b053 --- /dev/null +++ b/client/src/components/DataFiles/DataFilesTest.jsx @@ -0,0 +1,27 @@ +import { React, useEffect } from 'react'; + +import LoadingSpinner from '_common/LoadingSpinner'; +import { useExternalStyles } from 'hooks/datafiles'; + +function DataTable() { + const { hostRef, areStylesLoaded, renderWithStyles } = useExternalStyles(); + + useEffect(() => { + if (areStylesLoaded) { + renderWithStyles( +
+

Browse Datasets

+

Sample content.

+
+ ); + } + }, [areStylesLoaded, renderWithStyles]); + + if (!areStylesLoaded) { + return ; + } + + return
; +} + +export default DataTable; diff --git a/client/src/components/DataFiles/index.js b/client/src/components/DataFiles/index.js index 86018e2c3c..d0cddb2995 100644 --- a/client/src/components/DataFiles/index.js +++ b/client/src/components/DataFiles/index.js @@ -1 +1 @@ -export { default } from './DataFiles'; +export { default } from './DataFilesTest'; diff --git a/client/src/hooks/datafiles/index.js b/client/src/hooks/datafiles/index.js index d238f754bf..92a9494d73 100644 --- a/client/src/hooks/datafiles/index.js +++ b/client/src/hooks/datafiles/index.js @@ -1,5 +1,6 @@ export { default as useSystemDisplayName } from './useSystemDisplayName'; export { default as useSelectedFiles } from './useSelectedFiles'; +export { default as useExternalStyles } from './useExternalStyles'; export { default as useFileListing } from './useFileListing'; export { default as useSystems } from './useSystems'; export { default as useModal } from './useModal'; diff --git a/client/src/hooks/datafiles/useExternalStyles.jsx b/client/src/hooks/datafiles/useExternalStyles.jsx new file mode 100644 index 0000000000..bc0260aed1 --- /dev/null +++ b/client/src/hooks/datafiles/useExternalStyles.jsx @@ -0,0 +1,71 @@ +import { useEffect, useRef, useState } from 'react'; +import { createRoot } from 'react-dom/client'; + +// The shared set of external stylesheets for your components +const EXTERNAL_STYLESHEETS = [ + 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600', + 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css', + // Add your actual stylesheet URLs here +]; + +function useExternalStyles() { + const hostRef = useRef(); + const shadowRootRef = useRef(); + const reactRootRef = useRef(); + const [areStylesLoaded, setAreStylesLoaded] = useState(false); + + useEffect(() => { + if (!hostRef.current) return; + + // To isolate styles to only apply to what we render + shadowRootRef.current = hostRef.current.attachShadow({ mode: 'open' }); + + let loadedStylesheetsCount = 0; + const totalStylesheets = EXTERNAL_STYLESHEETS.length; + const checkAllLoaded = () => { + loadedStylesheetsCount++; + if (loadedStylesheetsCount === totalStylesheets) { + setAreStylesLoaded(true); + } + }; + + EXTERNAL_STYLESHEETS.forEach(url => { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = url; + link.onload = checkAllLoaded; + link.onerror = checkAllLoaded; // Still proceed even if one fails + shadowRootRef.current.appendChild(link); + }); + + if (totalStylesheets === 0) { + setStylesLoaded(true); + } + + return () => { + if (reactRootRef.current) { + reactRootRef.current.unmount(); + } + }; + }, []); + + const renderWithStyles = (children) => { + if (!areStylesLoaded || !shadowRootRef.current) return; + + if (!reactRootRef.current) { + const container = document.createElement('div'); + shadowRootRef.current.appendChild(container); + reactRootRef.current = createRoot(container); + } + + reactRootRef.current.render(children); + }; + + return { + hostRef, + areStylesLoaded, + renderWithStyles, + }; +} + +export default useExternalStyles; \ No newline at end of file From 77c204e8840884491e2e4cbb8e83d6891622842b Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Wed, 18 Jun 2025 10:01:59 -0500 Subject: [PATCH 02/79] fix: WC-219 change hook ext from jsx to js --- .../datafiles/{useExternalStyles.jsx => useExternalStyles.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/src/hooks/datafiles/{useExternalStyles.jsx => useExternalStyles.js} (100%) diff --git a/client/src/hooks/datafiles/useExternalStyles.jsx b/client/src/hooks/datafiles/useExternalStyles.js similarity index 100% rename from client/src/hooks/datafiles/useExternalStyles.jsx rename to client/src/hooks/datafiles/useExternalStyles.js From c0050ad82d896c36e218c209703e27cc5098ca72 Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Wed, 18 Jun 2025 10:58:49 -0500 Subject: [PATCH 03/79] fix: WC-219 fix hot reload error, add test The testing might be overkill. It passed before the fix was added. --- .../components/DataFiles/DataFilesTest.jsx | 19 +- .../src/hooks/datafiles/useExternalStyles.js | 109 +++++++--- .../datafiles/useExternalStyles.test.jsx | 194 ++++++++++++++++++ 3 files changed, 292 insertions(+), 30 deletions(-) create mode 100644 client/src/hooks/datafiles/useExternalStyles.test.jsx diff --git a/client/src/components/DataFiles/DataFilesTest.jsx b/client/src/components/DataFiles/DataFilesTest.jsx index 419452b053..ffda0591dd 100644 --- a/client/src/components/DataFiles/DataFilesTest.jsx +++ b/client/src/components/DataFiles/DataFilesTest.jsx @@ -1,24 +1,31 @@ import { React, useEffect } from 'react'; -import LoadingSpinner from '_common/LoadingSpinner'; +import { LoadingSpinner, InlineMessage } from '_common'; import { useExternalStyles } from 'hooks/datafiles'; function DataTable() { - const { hostRef, areStylesLoaded, renderWithStyles } = useExternalStyles(); + const { hostRef, styleStatus, renderWithStyles } = useExternalStyles(); useEffect(() => { - if (areStylesLoaded) { + // IDEA: How about change these to single value like `isReadyToRenderWithStyles`? + if (hostRef.current && styleStatus.completed) { renderWithStyles(

Browse Datasets

+ {styleStatus.failed.length > 0 && ( + + Some styles failed to load. UI may look incorrect. + + )}

Sample content.

); } - }, [areStylesLoaded, renderWithStyles]); + }, [hostRef.current, styleStatus, renderWithStyles]); - if (!areStylesLoaded) { - return ; + + if (!styleStatus.completed) { + return
; } return
; diff --git a/client/src/hooks/datafiles/useExternalStyles.js b/client/src/hooks/datafiles/useExternalStyles.js index bc0260aed1..6d29054d6e 100644 --- a/client/src/hooks/datafiles/useExternalStyles.js +++ b/client/src/hooks/datafiles/useExternalStyles.js @@ -1,69 +1,130 @@ import { useEffect, useRef, useState } from 'react'; import { createRoot } from 'react-dom/client'; -// The shared set of external stylesheets for your components -const EXTERNAL_STYLESHEETS = [ +const DEFAULT_STYLESHEETS = [ 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600', 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css', - // Add your actual stylesheet URLs here ]; -function useExternalStyles() { +function useExternalStyles(externalStylesheets = DEFAULT_STYLESHEETS) { const hostRef = useRef(); const shadowRootRef = useRef(); const reactRootRef = useRef(); - const [areStylesLoaded, setAreStylesLoaded] = useState(false); + const [styleStatus, setStyleStatus] = useState({ + loaded: [], + failed: [], + completed: false + }); useEffect(() => { - if (!hostRef.current) return; + if (!hostRef.current) { + console.warn('No hostRef.current found'); + return; + } - // To isolate styles to only apply to what we render - shadowRootRef.current = hostRef.current.attachShadow({ mode: 'open' }); + if (!shadowRootRef.current) { + try { + shadowRootRef.current = hostRef.current.shadowRoot || + hostRef.current.attachShadow({ mode: 'open' }); + } catch (error) { + console.error('Failed to create shadow root:', error); + return; + } + } - let loadedStylesheetsCount = 0; - const totalStylesheets = EXTERNAL_STYLESHEETS.length; - const checkAllLoaded = () => { - loadedStylesheetsCount++; - if (loadedStylesheetsCount === totalStylesheets) { - setAreStylesLoaded(true); + const totalStylesheets = externalStylesheets.length; + + const checkAllComplete = (loaded, failed) => { + if (loaded.length + failed.length === totalStylesheets) { + console.log('Style loading complete:', { + succeeded: loaded, + failed: failed + }); + setStyleStatus({ + loaded, + failed, + completed: true + }); } }; - EXTERNAL_STYLESHEETS.forEach(url => { + if (totalStylesheets === 0) { + setStyleStatus({ loaded: [], failed: [], completed: true }); + return; + } + + // Remove any existing stylesheets before adding new ones + const existingLinks = shadowRootRef.current.querySelectorAll('link'); + existingLinks.forEach(link => link.remove()); + + const loaded = []; + const failed = []; + + externalStylesheets.forEach(url => { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = url; - link.onload = checkAllLoaded; - link.onerror = checkAllLoaded; // Still proceed even if one fails + link.onload = () => { + loaded.push(url); + checkAllComplete(loaded, failed); + }; + link.onerror = (error) => { + console.error(`Failed to load stylesheet: ${url}`, error); + failed.push(url); + checkAllComplete(loaded, failed); + }; shadowRootRef.current.appendChild(link); }); - if (totalStylesheets === 0) { - setStylesLoaded(true); - } - return () => { if (reactRootRef.current) { + // Unmount the React root first reactRootRef.current.unmount(); + reactRootRef.current = null; } + + // Clean up stylesheets + const links = shadowRootRef.current?.querySelectorAll('link'); + links?.forEach(link => link.remove()); + + // Reset the status + setStyleStatus({ + loaded: [], + failed: [], + completed: false + }); }; - }, []); + }, [externalStylesheets]); const renderWithStyles = (children) => { - if (!areStylesLoaded || !shadowRootRef.current) return; + if (!shadowRootRef.current) { + console.warn('Cannot render: no shadow root'); + return; + } if (!reactRootRef.current) { + // Clean up any existing container + const existingContainer = shadowRootRef.current.querySelector('div'); + if (existingContainer) { + existingContainer.remove(); + } + const container = document.createElement('div'); shadowRootRef.current.appendChild(container); reactRootRef.current = createRoot(container); } + if (styleStatus.failed.length > 0) { + console.warn('Rendering with missing styles:', styleStatus.failed); + } + reactRootRef.current.render(children); }; return { hostRef, - areStylesLoaded, + areStylesLoaded: styleStatus.completed, + styleStatus, renderWithStyles, }; } diff --git a/client/src/hooks/datafiles/useExternalStyles.test.jsx b/client/src/hooks/datafiles/useExternalStyles.test.jsx new file mode 100644 index 0000000000..98cda5c831 --- /dev/null +++ b/client/src/hooks/datafiles/useExternalStyles.test.jsx @@ -0,0 +1,194 @@ +import React from 'react'; +import { render, act, cleanup } from '@testing-library/react'; +import { vi } from 'vitest'; +import useExternalStyles from './useExternalStyles'; + +const TEST_STYLESHEETS = [ + 'test://style1.css', + 'test://style2.css' +]; + +describe('useExternalStyles', () => { + beforeEach(() => { + // Mock the link.onload event to fire immediately + const originalCreateElement = document.createElement; + vi.spyOn(document, 'createElement').mockImplementation((tagName) => { + const element = originalCreateElement.call(document, tagName); + if (tagName === 'link') { + // Simulate successful load immediately + setTimeout(() => element.onload(), 0); + } + return element; + }); + }); + + afterEach(() => { + cleanup(); + vi.clearAllMocks(); + vi.restoreAllMocks(); + }); + + test('useExternalStyles hook initializes correctly', () => { + let hookResult; + const TestComponent = () => { + hookResult = useExternalStyles(TEST_STYLESHEETS); + return
Test Content
; + }; + + act(() => { + render(); + }); + + expect(hookResult).toBeDefined(); + expect(hookResult.hostRef).toBeDefined(); + expect(hookResult.areStylesLoaded).toBeDefined(); + expect(hookResult.styleStatus).toBeDefined(); + expect(hookResult.renderWithStyles).toBeDefined(); + }); + + test('handles rapid unmount and remount during hot reload', async () => { + let hookResult; + const TestComponent = () => { + hookResult = useExternalStyles(TEST_STYLESHEETS); + return
Test Content
; + }; + + // Initial render + const { unmount } = render(); + + // Wait for initial style loading + await act(async () => { + await new Promise(resolve => setTimeout(resolve, 0)); + }); + + // Unmount + act(() => { + unmount(); + }); + + // Remount immediately + act(() => { + render(); + }); + + // Wait for style loading after remount + await act(async () => { + await new Promise(resolve => setTimeout(resolve, 0)); + }); + + expect(hookResult.styleStatus.completed).toBe(true); + expect(hookResult.styleStatus.loaded).toEqual(TEST_STYLESHEETS); + }); + + test('cleanup happens properly during unmount', () => { + let hookResult; + const unmountSpy = vi.fn(); + + const TestComponent = () => { + hookResult = useExternalStyles(TEST_STYLESHEETS); + React.useEffect(() => { + return () => unmountSpy(); + }, []); + return
Test Content
; + }; + + const { unmount } = render(); + + act(() => { + hookResult.renderWithStyles(
Test
); + }); + + act(() => { + unmount(); + }); + + expect(unmountSpy).toHaveBeenCalled(); + }); + + test('handles style loading errors gracefully', async () => { + // Mock createElement to simulate a failed load + const originalCreateElement = document.createElement; + vi.spyOn(document, 'createElement').mockImplementation((tagName) => { + const element = originalCreateElement.call(document, tagName); + if (tagName === 'link') { + // Simulate failed load immediately + setTimeout(() => element.onerror(new Error('Failed to load')), 0); + } + return element; + }); + + let hookResult; + const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + + const TestComponent = () => { + hookResult = useExternalStyles(TEST_STYLESHEETS); + return
Test Content
; + }; + + render(); + + // Wait for error handling + await act(async () => { + await new Promise(resolve => setTimeout(resolve, 0)); + }); + + expect(hookResult.styleStatus.completed).toBe(true); + expect(hookResult.styleStatus.failed).toEqual(TEST_STYLESHEETS); + errorSpy.mockRestore(); + }); + + test('handles concurrent renders during hot reload', async () => { + let hookResult; + const TestComponent = () => { + hookResult = useExternalStyles(TEST_STYLESHEETS); + return
Test Content
; + }; + + const { rerender } = render(); + + // Wait for initial style loading + await act(async () => { + await new Promise(resolve => setTimeout(resolve, 0)); + }); + + // Trigger multiple rerenders + for (let i = 0; i < 3; i++) { + act(() => { + rerender(); + }); + } + + // Wait for final style loading + await act(async () => { + await new Promise(resolve => setTimeout(resolve, 0)); + }); + + expect(hookResult.styleStatus.completed).toBe(true); + expect(hookResult.styleStatus.loaded).toEqual(TEST_STYLESHEETS); + }); + + test('handles unmount during style loading', async () => { + let hookResult; + const TestComponent = () => { + hookResult = useExternalStyles(TEST_STYLESHEETS); + return
Test Content
; + }; + + const { unmount } = render(); + + // Start style loading + act(() => { + hookResult.renderWithStyles(
Test
); + }); + + // Unmount during loading + act(() => { + unmount(); + }); + + // Should be able to remount without errors + act(() => { + render(); + }); + }); +}); \ No newline at end of file From d4950a4db2bcce53c01baf42e4208354165bc19d Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Wed, 18 Jun 2025 12:16:15 -0500 Subject: [PATCH 04/79] refactor: WC-219 organize code more cleanly to help in debugging solution to hot reload error in dev --- .../src/hooks/datafiles/useExternalStyles.js | 126 ++++++++++-------- 1 file changed, 67 insertions(+), 59 deletions(-) diff --git a/client/src/hooks/datafiles/useExternalStyles.js b/client/src/hooks/datafiles/useExternalStyles.js index 6d29054d6e..4015509afd 100644 --- a/client/src/hooks/datafiles/useExternalStyles.js +++ b/client/src/hooks/datafiles/useExternalStyles.js @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState, useLayoutEffect } from 'react'; import { createRoot } from 'react-dom/client'; const DEFAULT_STYLESHEETS = [ @@ -16,10 +16,10 @@ function useExternalStyles(externalStylesheets = DEFAULT_STYLESHEETS) { completed: false }); - useEffect(() => { + function initializeShadowRoot() { if (!hostRef.current) { console.warn('No hostRef.current found'); - return; + return null; } if (!shadowRootRef.current) { @@ -28,98 +28,106 @@ function useExternalStyles(externalStylesheets = DEFAULT_STYLESHEETS) { hostRef.current.attachShadow({ mode: 'open' }); } catch (error) { console.error('Failed to create shadow root:', error); - return; + return null; } } - const totalStylesheets = externalStylesheets.length; + return shadowRootRef.current; + } + + function cleanupDOM() { + if (shadowRootRef.current) { + const container = shadowRootRef.current.querySelector('div'); + if (container) container.remove(); + const links = shadowRootRef.current.querySelectorAll('link'); + links.forEach(link => link.remove()); + } + } + + function cleanupReact() { + if (reactRootRef.current) { + reactRootRef.current.unmount(); + reactRootRef.current = null; + } - const checkAllComplete = (loaded, failed) => { - if (loaded.length + failed.length === totalStylesheets) { - console.log('Style loading complete:', { - succeeded: loaded, - failed: failed - }); - setStyleStatus({ - loaded, - failed, - completed: true - }); - } - }; + setStyleStatus({ + loaded: [], + failed: [], + completed: false + }); + } + + useLayoutEffect(() => { + return cleanupDOM; + }, []); + + useEffect(() => { + const shadowRoot = initializeShadowRoot(); + if (!shadowRoot) return; + + loadStylesheets(shadowRoot); + + return cleanupReact; + }, [externalStylesheets]); + function loadStylesheets(shadowRoot) { + const totalStylesheets = externalStylesheets.length; if (totalStylesheets === 0) { setStyleStatus({ loaded: [], failed: [], completed: true }); return; } - // Remove any existing stylesheets before adding new ones - const existingLinks = shadowRootRef.current.querySelectorAll('link'); + const existingLinks = shadowRoot.querySelectorAll('link'); existingLinks.forEach(link => link.remove()); const loaded = []; const failed = []; + function checkAllComplete() { + if (loaded.length + failed.length === totalStylesheets) { + setStyleStatus({ + loaded, + failed, + completed: true + }); + if (failed.length > 0) { + console.warn('Some stylesheets failed to load:', failed); + } + } + } + externalStylesheets.forEach(url => { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = url; link.onload = () => { loaded.push(url); - checkAllComplete(loaded, failed); + checkAllComplete(); }; - link.onerror = (error) => { - console.error(`Failed to load stylesheet: ${url}`, error); + link.onerror = () => { failed.push(url); - checkAllComplete(loaded, failed); + checkAllComplete(); }; - shadowRootRef.current.appendChild(link); + shadowRoot.appendChild(link); }); + } - return () => { - if (reactRootRef.current) { - // Unmount the React root first - reactRootRef.current.unmount(); - reactRootRef.current = null; - } - - // Clean up stylesheets - const links = shadowRootRef.current?.querySelectorAll('link'); - links?.forEach(link => link.remove()); - - // Reset the status - setStyleStatus({ - loaded: [], - failed: [], - completed: false - }); - }; - }, [externalStylesheets]); - - const renderWithStyles = (children) => { - if (!shadowRootRef.current) { + function renderWithStyles(children) { + const shadowRoot = shadowRootRef.current; + if (!shadowRoot) { console.warn('Cannot render: no shadow root'); return; } if (!reactRootRef.current) { - // Clean up any existing container - const existingContainer = shadowRootRef.current.querySelector('div'); - if (existingContainer) { - existingContainer.remove(); - } - const container = document.createElement('div'); - shadowRootRef.current.appendChild(container); + shadowRoot.appendChild(container); reactRootRef.current = createRoot(container); } - if (styleStatus.failed.length > 0) { - console.warn('Rendering with missing styles:', styleStatus.failed); - } - reactRootRef.current.render(children); - }; + } + return { hostRef, From b1dfb850ea686b283fca447669cb0847ca3bfbcb Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Wed, 18 Jun 2025 17:53:55 -0500 Subject: [PATCH 05/79] refactor: WC-219 change solution from DOM to CSS KNOWN ISSUES: Does not make CMS content look exactly correct. --- ...esTest.jsx => DataFilesTestShadowRoot.jsx} | 4 +- .../DataFiles/DataFilesTestStyleConflict.jsx | 322 ++++++++++++++++++ client/src/components/DataFiles/index.js | 2 +- client/src/index.css | 6 +- server/portal/templates/base.html | 11 +- 5 files changed, 336 insertions(+), 9 deletions(-) rename client/src/components/DataFiles/{DataFilesTest.jsx => DataFilesTestShadowRoot.jsx} (94%) create mode 100644 client/src/components/DataFiles/DataFilesTestStyleConflict.jsx diff --git a/client/src/components/DataFiles/DataFilesTest.jsx b/client/src/components/DataFiles/DataFilesTestShadowRoot.jsx similarity index 94% rename from client/src/components/DataFiles/DataFilesTest.jsx rename to client/src/components/DataFiles/DataFilesTestShadowRoot.jsx index ffda0591dd..edc089cdcf 100644 --- a/client/src/components/DataFiles/DataFilesTest.jsx +++ b/client/src/components/DataFiles/DataFilesTestShadowRoot.jsx @@ -3,7 +3,7 @@ import { React, useEffect } from 'react'; import { LoadingSpinner, InlineMessage } from '_common'; import { useExternalStyles } from 'hooks/datafiles'; -function DataTable() { +function DataGallery() { const { hostRef, styleStatus, renderWithStyles } = useExternalStyles(); useEffect(() => { @@ -31,4 +31,4 @@ function DataTable() { return
; } -export default DataTable; +export default DataGallery; diff --git a/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx b/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx new file mode 100644 index 0000000000..18b81e949c --- /dev/null +++ b/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx @@ -0,0 +1,322 @@ +import { React, useEffect } from 'react'; + +import { LoadingSpinner, InlineMessage } from '_common'; +import { useExternalStyles } from 'hooks/datafiles'; + +const externalCSS = ` + @layer mimic-cms.base, mimic-cms.project, mimic-cms.revert; + + @import url(/static/site_cms/css/build/core-styles.base.css) layer(mimic-cms.base); + @import url(/static/site_cms/css/build/core-styles.cms.css) layer(mimic-cms.base); + @import url(/static/site_cms/css/build/core-cms.css) layer(mimic-cms.project); + + @import url(https://cdn.jsdelivr.net/gh/TACC/Core-CMS-Custom@5717c8d/digitalrocks_assets/css/cms.css) layer(mimic-cms); + @import url(https://cdn.jsdelivr.net/gh/TACC/Core-CMS-Custom@5717c8d/digitalrocks_assets/css/for-core-styles.css) layer(mimic-cms); +`; + +const revertCSS = ` + /* To preserve .nav-sidebar appearance */ + /* WARNING: Does not make CMS content look exactly correct */ + /* WARNING: Makes CMS content looks incorrect in a different way */ + @layer mimic-cms.revert { + html, + body { + all: revert; + } + body { + /* NOTE: Effect is only noticeable if nav-sidebar link text wraps */ + line-height: var(--bs-body-line-height); + } + + .nav-sidebar, + .nav-sidebar * { + all: revert-layer; + } + } +`; + +function DataGallery() { + return (<> + {% if settings.PORTAL_CSS_FILENAMES|length %} {% for stylesheet in settings.PORTAL_CSS_FILENAMES %} {% endfor %} {% endif %} - - - - - {% block styles %}{% endblock %} From 84c0c7bb46f983ace4819deab7f28c8be2eaf253 Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Wed, 18 Jun 2025 17:54:46 -0500 Subject: [PATCH 06/79] chore: WC-219 remove cruft --- .../src/components/DataFiles/DataFilesTestStyleConflict.jsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx b/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx index 18b81e949c..91e89999c8 100644 --- a/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx +++ b/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx @@ -1,7 +1,4 @@ -import { React, useEffect } from 'react'; - -import { LoadingSpinner, InlineMessage } from '_common'; -import { useExternalStyles } from 'hooks/datafiles'; +import { React } from 'react'; const externalCSS = ` @layer mimic-cms.base, mimic-cms.project, mimic-cms.revert; From 30cff34dc5dab671bf6bf90b62b2e8f6084da2e5 Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Wed, 18 Jun 2025 18:01:03 -0500 Subject: [PATCH 07/79] feat: WC-219 hide navbar, simplify cms content CSS KNOWN ISSUE: CMS content still does not render perfect. --- .../DataFiles/DataFilesTestStyleConflict.jsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx b/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx index 91e89999c8..22210b9c59 100644 --- a/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx +++ b/client/src/components/DataFiles/DataFilesTestStyleConflict.jsx @@ -1,5 +1,11 @@ import { React } from 'react'; +const temporaryCSS = ` + .nav-sidebar { + display: none; + } +`; + const externalCSS = ` @layer mimic-cms.base, mimic-cms.project, mimic-cms.revert; @@ -12,21 +18,14 @@ const externalCSS = ` `; const revertCSS = ` - /* To preserve .nav-sidebar appearance */ - /* WARNING: Does not make CMS content look exactly correct */ - /* WARNING: Makes CMS content looks incorrect in a different way */ - @layer mimic-cms.revert { - html, - body { + @layer mimic-cms { + /* To revert Portal styles */ + #react-root, #react-root * { all: revert; } - body { - /* NOTE: Effect is only noticeable if nav-sidebar link text wraps */ - line-height: var(--bs-body-line-height); - } - .nav-sidebar, - .nav-sidebar * { + /* To re-apply CMS styles */ + #mimic-cms, #mimic-cms * { all: revert-layer; } } @@ -34,6 +33,7 @@ const revertCSS = ` function DataGallery() { return (<> + {% if settings.PORTAL_CSS_FILENAMES|length %} From 4d035eae97afed273d229e659130f2e5c9ecb4be Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:07:42 -0500 Subject: [PATCH 17/79] fix: basic text color too dark cuz it is using portal basic text color instead of cms basic text color --- .../components/DataFiles/DataFilesForDPMWithCMSStyles.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/src/components/DataFiles/DataFilesForDPMWithCMSStyles.jsx b/client/src/components/DataFiles/DataFilesForDPMWithCMSStyles.jsx index 6fdb65ca1d..81ebcd5a03 100644 --- a/client/src/components/DataFiles/DataFilesForDPMWithCMSStyles.jsx +++ b/client/src/components/DataFiles/DataFilesForDPMWithCMSStyles.jsx @@ -17,6 +17,11 @@ const externalCSS = ` `; const revertCSS = ` + /* To restore CMS body color */ + .workbench-wrapper { + color: var(--global-color-primary--x-dark); + } + /* To restore relevant behavior of Bootstrap grid */ .workbench-content .container { /* To use padding from Bootstrap 4 (which CMS still uses) */ From 54c6376d04f7c61b5f4ded1dcd28efc1611f00fa Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:02:47 -0500 Subject: [PATCH 18/79] feat!: footer from CMS TO DO: - Make footer conditional. - Fix footer position. BREAKING CHANGE: Footer always shown. Footer position is wrong. --- .../DataFiles/DataFilesForDPMWithCMSStyles.module.css | 1 + server/conf/docker/docker-compose-dev.all.debug.yml | 2 +- server/portal/templates/includes/footer.html | 10 +++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 client/src/components/DataFiles/DataFilesForDPMWithCMSStyles.module.css diff --git a/client/src/components/DataFiles/DataFilesForDPMWithCMSStyles.module.css b/client/src/components/DataFiles/DataFilesForDPMWithCMSStyles.module.css new file mode 100644 index 0000000000..d7c678f672 --- /dev/null +++ b/client/src/components/DataFiles/DataFilesForDPMWithCMSStyles.module.css @@ -0,0 +1 @@ +@import ('@tacc/core-styles/dist/trumps/s-footer.css') layer(mimic-cms.project); diff --git a/server/conf/docker/docker-compose-dev.all.debug.yml b/server/conf/docker/docker-compose-dev.all.debug.yml index fc239ce4ac..2f7c8339e1 100644 --- a/server/conf/docker/docker-compose-dev.all.debug.yml +++ b/server/conf/docker/docker-compose-dev.all.debug.yml @@ -3,7 +3,7 @@ --- services: cms: - image: taccwma/core-cms:latest + image: taccwma/core-cms:feat-WC-274-serve-footer-markup # e46d45d5 volumes: - ../cms/secrets.py:/code/taccsite_cms/secrets.py - ../cms/uwsgi/uwsgi.ini:/code/uwsgi.ini diff --git a/server/portal/templates/includes/footer.html b/server/portal/templates/includes/footer.html index 479c01109c..30f3107eb2 100644 --- a/server/portal/templates/includes/footer.html +++ b/server/portal/templates/includes/footer.html @@ -1,3 +1,11 @@ {% load static %} -