From 68ffacd8e10d5ea3ad4ab1dc1edcbc334f7f8f37 Mon Sep 17 00:00:00 2001 From: Balint Kleri <b@balintbrews.com> Date: Thu, 1 May 2025 14:52:13 +0200 Subject: [PATCH 1/6] Create drupal-globals utility --- ui/src/features/code-editor/Preview.tsx | 16 ++++------------ ui/src/utils/drupal-globals.ts | 6 ++++++ 2 files changed, 10 insertions(+), 12 deletions(-) create mode 100644 ui/src/utils/drupal-globals.ts diff --git a/ui/src/features/code-editor/Preview.tsx b/ui/src/features/code-editor/Preview.tsx index 6de5083d35..4fe17c21a8 100644 --- a/ui/src/features/code-editor/Preview.tsx +++ b/ui/src/features/code-editor/Preview.tsx @@ -38,19 +38,11 @@ import { getSlotNamesForPreview, } from '@/features/code-editor/utils'; import { useGetCodeComponentsQuery } from '@/services/componentAndLayout'; +import { getDrupal, getXbSettings, getBasePath } from '@/utils/drupal-globals'; -const { Drupal } = window as any; - -const XB_MODULE_UI_PATH = (() => { - const { drupalSettings } = window; - if (!drupalSettings) { - return ''; - } - const { xbModulePath } = drupalSettings.xb; - const { baseUrl } = drupalSettings.path; - return `${baseUrl}${xbModulePath}/ui` as const; -})(); - +const Drupal = getDrupal(); +const XB_MODULE_UI_PATH = + `${getBasePath()}${getXbSettings().xbModulePath}/ui` as const; const PREVIEW_LIB_PATH = 'dist/assets/code-editor-preview.js' as const; const swcConfig: Options = { diff --git a/ui/src/utils/drupal-globals.ts b/ui/src/utils/drupal-globals.ts new file mode 100644 index 0000000000..6215e5aa1b --- /dev/null +++ b/ui/src/utils/drupal-globals.ts @@ -0,0 +1,6 @@ +const { Drupal, drupalSettings } = window as any; + +export const getDrupal = () => Drupal; +export const getDrupalSettings = () => drupalSettings; +export const getXbSettings = () => drupalSettings.xb; +export const getBasePath = () => drupalSettings.path.baseUrl; -- GitLab From 11620cf980def1f37a45c82834690af0ff08e006 Mon Sep 17 00:00:00 2001 From: Harumi Jang <harumi.jang@acquia.com> Date: Mon, 12 May 2025 12:47:50 -0400 Subject: [PATCH 2/6] Change drupalSettings --- ui/src/components/ComponentPreview.tsx | 3 ++- .../components/extensions/ExtensionsList.tsx | 25 +++++++++++-------- .../form/components/drupal/DrupalTextArea.tsx | 3 ++- ui/src/components/pageInfo/PageInfo.tsx | 4 +-- ui/src/components/topbar/Topbar.tsx | 3 ++- ui/src/features/drupal/drupalUtil.ts | 3 ++- ui/src/features/layout/preview/Preview.tsx | 3 ++- ui/src/hooks/useComponentSelection.ts | 3 ++- ui/src/hooks/useEditorNavigation.ts | 4 +-- ui/src/main.tsx | 3 ++- ui/src/services/addAjaxPageState.ts | 4 ++- 11 files changed, 35 insertions(+), 23 deletions(-) diff --git a/ui/src/components/ComponentPreview.tsx b/ui/src/components/ComponentPreview.tsx index fc0141c1f7..c768a0bb9e 100644 --- a/ui/src/components/ComponentPreview.tsx +++ b/ui/src/components/ComponentPreview.tsx @@ -5,12 +5,13 @@ import clsx from 'clsx'; import Panel from '@/components/Panel'; import type { XBComponent } from '@/types/Component'; import type { Section } from '@/types/Section'; +import { getDrupalSettings } from '@/utils/drupal-globals'; interface ComponentPreviewProps { componentListItem: XBComponent | Section; } -const { drupalSettings } = window; +const drupalSettings = getDrupalSettings(); const ComponentPreview: React.FC<ComponentPreviewProps> = ({ componentListItem, diff --git a/ui/src/components/extensions/ExtensionsList.tsx b/ui/src/components/extensions/ExtensionsList.tsx index 6ca53731e8..9dfe17031e 100644 --- a/ui/src/components/extensions/ExtensionsList.tsx +++ b/ui/src/components/extensions/ExtensionsList.tsx @@ -3,24 +3,27 @@ import { ExternalLinkIcon } from '@radix-ui/react-icons'; import ExtensionButton from '@/components/extensions/ExtensionButton'; import { handleNonWorkingBtn } from '@/utils/function-utils'; import type React from 'react'; +import { getDrupalSettings } from '@/utils/drupal-globals'; interface ExtensionsPopoverProps {} -const { drupalSettings } = window; +const drupalSettings = getDrupalSettings(); const ExtensionsList: React.FC<ExtensionsPopoverProps> = () => { let extensionsList = []; if (drupalSettings && drupalSettings.xbExtension) { - extensionsList = Object.values(drupalSettings.xbExtension).map((value) => { - return { - ...value, - imgSrc: - value.imgSrc || - `${drupalSettings.path.baseUrl}${drupalSettings.xb.xbModulePath}/ui/assets/icons/extension-default-abstract.svg`, - name: value.name, - description: value.description, - }; - }); + extensionsList = Object.values(drupalSettings.xbExtension).map( + (value: any) => { + return { + ...value, + imgSrc: + value.imgSrc || + `${drupalSettings.path.baseUrl}${drupalSettings.xb.xbModulePath}/ui/assets/icons/extension-default-abstract.svg`, + name: value.name, + description: value.description, + }; + }, + ); } return <ExtensionsListDisplay extensions={extensionsList || []} />; diff --git a/ui/src/components/form/components/drupal/DrupalTextArea.tsx b/ui/src/components/form/components/drupal/DrupalTextArea.tsx index 2a6b49cb19..7456269988 100644 --- a/ui/src/components/form/components/drupal/DrupalTextArea.tsx +++ b/ui/src/components/form/components/drupal/DrupalTextArea.tsx @@ -5,7 +5,8 @@ import { useRef, useState } from 'react'; import { Flex } from '@radix-ui/themes'; import type { Attributes } from '@/types/DrupalAttribute'; import DrupalFormattedTextArea from './DrupalFormattedTextArea'; -const { drupalSettings } = window as any; +import { getDrupalSettings } from '@/utils/drupal-globals'; +const drupalSettings = getDrupalSettings(); const DrupalTextArea = ({ attributes = {}, diff --git a/ui/src/components/pageInfo/PageInfo.tsx b/ui/src/components/pageInfo/PageInfo.tsx index dbd92beef0..be023da0fd 100644 --- a/ui/src/components/pageInfo/PageInfo.tsx +++ b/ui/src/components/pageInfo/PageInfo.tsx @@ -40,7 +40,7 @@ import { selectEntityId, selectEntityType, } from '@/features/configuration/configurationSlice'; -import { getBaseUrl } from '@/utils/drupal-globals'; +import { getBaseUrl, getDrupalSettings } from '@/utils/drupal-globals'; interface PageType { [key: string]: ReactElement; @@ -53,7 +53,7 @@ const iconMap: PageType = { GlobalSectionName: <SectionIcon />, }; -const { drupalSettings } = window; +const drupalSettings = getDrupalSettings(); const PageInfo = () => { const { showBoundary } = useErrorBoundary(); diff --git a/ui/src/components/topbar/Topbar.tsx b/ui/src/components/topbar/Topbar.tsx index 2978007940..4d07e9e351 100644 --- a/ui/src/components/topbar/Topbar.tsx +++ b/ui/src/components/topbar/Topbar.tsx @@ -21,10 +21,11 @@ import ExtensionsList from '@/components/extensions/ExtensionsList'; import TopbarPopover from '@/components/topbar/menu/TopbarPopover'; import topBarStyles from '@/components/topbar/Topbar.module.css'; import DynamicComponents from '@/components/dynamicComponents/DynamicComponents'; +import { getDrupalSettings } from '@/utils/drupal-globals'; const PREVIOUS_URL_STORAGE_KEY = 'XBPreviousURL'; -const { drupalSettings } = window; +const drupalSettings = getDrupalSettings(); const Topbar = () => { const navigate = useNavigate(); diff --git a/ui/src/features/drupal/drupalUtil.ts b/ui/src/features/drupal/drupalUtil.ts index 7a66dd17f6..be3e7c9e3c 100644 --- a/ui/src/features/drupal/drupalUtil.ts +++ b/ui/src/features/drupal/drupalUtil.ts @@ -1,6 +1,7 @@ import type { PropsValues } from '@/types/Form'; +import { getDrupalSettings } from '@/utils/drupal-globals'; -const { drupalSettings } = window; +const drupalSettings = getDrupalSettings(); export const setXbDrupalSetting = ( property: 'layoutUtils' | 'navUtils', value: PropsValues, diff --git a/ui/src/features/layout/preview/Preview.tsx b/ui/src/features/layout/preview/Preview.tsx index 6d23b26ce2..e864a68905 100644 --- a/ui/src/features/layout/preview/Preview.tsx +++ b/ui/src/features/layout/preview/Preview.tsx @@ -18,6 +18,7 @@ import { selectPageData } from '@/features/pageData/pageDataSlice'; import { selectPreviewHtml } from '@/features/pagePreview/previewSlice'; import { contentApi } from '@/services/content'; import { selectSelectedComponentUuid } from '@/features/ui/uiSlice'; +import { getDrupalSettings } from '@/utils/drupal-globals'; interface PreviewProps {} @@ -33,7 +34,7 @@ const previewSizes = { name: 'Mobile', }, }; -const { drupalSettings } = window; +const drupalSettings = getDrupalSettings(); type PreviewSizeKey = keyof typeof previewSizes; const labelFormKey = `${drupalSettings.xb.entityTypeKeys.label}[0][value]`; diff --git a/ui/src/hooks/useComponentSelection.ts b/ui/src/hooks/useComponentSelection.ts index 2f0a20f2ca..5212b60c09 100644 --- a/ui/src/hooks/useComponentSelection.ts +++ b/ui/src/hooks/useComponentSelection.ts @@ -17,8 +17,9 @@ import { import type { RegionNode } from '@/features/layout/layoutModelSlice'; import { selectLayout } from '@/features/layout/layoutModelSlice'; import { selectDevMode } from '@/features/configuration/configurationSlice'; +import { getDrupalSettings } from '@/utils/drupal-globals'; -const { drupalSettings } = window; +const drupalSettings = getDrupalSettings(); /** * Filters out any components that are parents or children of components in the selection diff --git a/ui/src/hooks/useEditorNavigation.ts b/ui/src/hooks/useEditorNavigation.ts index db468a9365..c5ac7a62e6 100644 --- a/ui/src/hooks/useEditorNavigation.ts +++ b/ui/src/hooks/useEditorNavigation.ts @@ -1,9 +1,9 @@ import { useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { DEFAULT_REGION } from '@/features/ui/uiSlice'; -import { getBaseUrl } from '@/utils/drupal-globals'; +import { getBaseUrl, getDrupalSettings } from '@/utils/drupal-globals'; -const { drupalSettings } = window; +const drupalSettings = getDrupalSettings(); /** * Hook for editor navigation functions diff --git a/ui/src/main.tsx b/ui/src/main.tsx index ce94c11ff9..db2fc46830 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -20,6 +20,7 @@ import transforms from '@/utils/transforms'; import '@/styles/radix-themes'; import '@/styles/index.css'; import { AJAX_UPDATE_FORM_STATE_EVENT } from '@/types/Ajax'; +import { getDrupalSettings } from '@/utils/drupal-globals'; // Provide these dependencies as globals so extensions do not have redundant and // potentially conflicting dependencies. @@ -32,7 +33,7 @@ interface ProviderComponentProps { store: EnhancedStore; } -const { drupalSettings } = window; +const drupalSettings = getDrupalSettings(); const { Drupal } = window as any; const container = document.getElementById('experience-builder'); diff --git a/ui/src/services/addAjaxPageState.ts b/ui/src/services/addAjaxPageState.ts index 4f0af37487..fd082b8a3c 100644 --- a/ui/src/services/addAjaxPageState.ts +++ b/ui/src/services/addAjaxPageState.ts @@ -1,4 +1,6 @@ -const { drupalSettings } = window as any; +import { getDrupalSettings } from '@/utils/drupal-globals'; + +const drupalSettings = getDrupalSettings(); const addAjaxPageState = (query: string) => { // Drupal's AJAX API automatically adds ajaxPageState as a parameter, but -- GitLab From 17a5246b3b49eb5f27915dc18c59b74b073d78b4 Mon Sep 17 00:00:00 2001 From: Harumi Jang <harumi.jang@acquia.com> Date: Mon, 12 May 2025 12:49:37 -0400 Subject: [PATCH 3/6] Change getDrupal --- ui/src/components/form/formUtil.ts | 3 ++- ui/src/hooks/useDrupalBehaviors.ts | 3 ++- ui/src/main.tsx | 4 ++-- ui/src/services/processResponseAssets.ts | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ui/src/components/form/formUtil.ts b/ui/src/components/form/formUtil.ts index fbc40cdd07..a1f6ce1958 100644 --- a/ui/src/components/form/formUtil.ts +++ b/ui/src/components/form/formUtil.ts @@ -14,6 +14,7 @@ import type { TransformConfig, Transforms } from '@/utils/transforms'; import transforms from '@/utils/transforms'; import qs from 'qs'; import addDraft2019 from 'ajv-formats-draft2019'; +import { getDrupal } from '@/utils/drupal-globals'; const ajv = new Ajv(); addFormats(ajv); addDraft2019(ajv); @@ -338,7 +339,7 @@ export function getPropsValues( // Iterate through every item in form state that corresponds to // a component input to create propsValues, which will ultimately be // used to update this component's model. - const { Drupal } = (window as any) || { + const Drupal = getDrupal() || { Drupal: { xbTransforms: transforms }, }; const transformsList: Transforms = Drupal?.xbTransforms || transforms; diff --git a/ui/src/hooks/useDrupalBehaviors.ts b/ui/src/hooks/useDrupalBehaviors.ts index 0b95f17b70..a245db9f59 100644 --- a/ui/src/hooks/useDrupalBehaviors.ts +++ b/ui/src/hooks/useDrupalBehaviors.ts @@ -1,7 +1,8 @@ import { useEffect } from 'react'; import type { RefObject } from 'react'; +import { getDrupal } from '@/utils/drupal-globals'; -const { Drupal } = window as any; +const Drupal = getDrupal(); export function useDrupalBehaviors( ref: RefObject<HTMLElement>, diff --git a/ui/src/main.tsx b/ui/src/main.tsx index db2fc46830..4fdcd80fce 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -20,7 +20,7 @@ import transforms from '@/utils/transforms'; import '@/styles/radix-themes'; import '@/styles/index.css'; import { AJAX_UPDATE_FORM_STATE_EVENT } from '@/types/Ajax'; -import { getDrupalSettings } from '@/utils/drupal-globals'; +import { getDrupal, getDrupalSettings } from '@/utils/drupal-globals'; // Provide these dependencies as globals so extensions do not have redundant and // potentially conflicting dependencies. @@ -34,7 +34,7 @@ interface ProviderComponentProps { } const drupalSettings = getDrupalSettings(); -const { Drupal } = window as any; +const Drupal = getDrupal(); const container = document.getElementById('experience-builder'); diff --git a/ui/src/services/processResponseAssets.ts b/ui/src/services/processResponseAssets.ts index 3c9f54e8c5..c8c88430aa 100644 --- a/ui/src/services/processResponseAssets.ts +++ b/ui/src/services/processResponseAssets.ts @@ -1,6 +1,7 @@ import type { PropsValues } from '@/types/Form'; +import { getDrupal } from '@/utils/drupal-globals'; -const { Drupal } = window as any; +const Drupal = getDrupal(); /** * Takes a response rendered by XBTemplateRenderer, identifies any attached -- GitLab From b04726b24fcdd5a391e619765c7b22a5a892c500 Mon Sep 17 00:00:00 2001 From: Harumi Jang <harumi.jang@acquia.com> Date: Mon, 12 May 2025 13:09:13 -0400 Subject: [PATCH 4/6] Use xbSettings --- ui/src/components/pageInfo/PageInfo.tsx | 6 +++--- ui/src/features/layout/preview/Preview.tsx | 6 +++--- ui/src/hooks/useComponentSelection.ts | 6 +++--- ui/src/hooks/useEditorNavigation.ts | 6 +++--- ui/src/utils/drupal-globals.ts | 1 - 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/ui/src/components/pageInfo/PageInfo.tsx b/ui/src/components/pageInfo/PageInfo.tsx index be023da0fd..cf206b314f 100644 --- a/ui/src/components/pageInfo/PageInfo.tsx +++ b/ui/src/components/pageInfo/PageInfo.tsx @@ -40,7 +40,7 @@ import { selectEntityId, selectEntityType, } from '@/features/configuration/configurationSlice'; -import { getBaseUrl, getDrupalSettings } from '@/utils/drupal-globals'; +import { getBaseUrl, getXbSettings } from '@/utils/drupal-globals'; interface PageType { [key: string]: ReactElement; @@ -53,7 +53,7 @@ const iconMap: PageType = { GlobalSectionName: <SectionIcon />, }; -const drupalSettings = getDrupalSettings(); +const xbSettings = getXbSettings(); const PageInfo = () => { const { showBoundary } = useErrorBoundary(); @@ -68,7 +68,7 @@ const PageInfo = () => { )?.name; const entity_form_fields = useAppSelector(selectPageData); const title = - entity_form_fields[`${drupalSettings.xb.entityTypeKeys.label}[0][value]`]; + entity_form_fields[`${xbSettings.entityTypeKeys.label}[0][value]`]; const { data: pageItems, diff --git a/ui/src/features/layout/preview/Preview.tsx b/ui/src/features/layout/preview/Preview.tsx index e864a68905..010011d9e4 100644 --- a/ui/src/features/layout/preview/Preview.tsx +++ b/ui/src/features/layout/preview/Preview.tsx @@ -18,7 +18,7 @@ import { selectPageData } from '@/features/pageData/pageDataSlice'; import { selectPreviewHtml } from '@/features/pagePreview/previewSlice'; import { contentApi } from '@/services/content'; import { selectSelectedComponentUuid } from '@/features/ui/uiSlice'; -import { getDrupalSettings } from '@/utils/drupal-globals'; +import { getXbSettings } from '@/utils/drupal-globals'; interface PreviewProps {} @@ -34,9 +34,9 @@ const previewSizes = { name: 'Mobile', }, }; -const drupalSettings = getDrupalSettings(); +const xbSettings = getXbSettings(); type PreviewSizeKey = keyof typeof previewSizes; -const labelFormKey = `${drupalSettings.xb.entityTypeKeys.label}[0][value]`; +const labelFormKey = `${xbSettings.entityTypeKeys.label}[0][value]`; const Preview: React.FC<PreviewProps> = () => { const layout = useAppSelector(selectLayout); diff --git a/ui/src/hooks/useComponentSelection.ts b/ui/src/hooks/useComponentSelection.ts index 5212b60c09..c2c35d1a8c 100644 --- a/ui/src/hooks/useComponentSelection.ts +++ b/ui/src/hooks/useComponentSelection.ts @@ -17,9 +17,9 @@ import { import type { RegionNode } from '@/features/layout/layoutModelSlice'; import { selectLayout } from '@/features/layout/layoutModelSlice'; import { selectDevMode } from '@/features/configuration/configurationSlice'; -import { getDrupalSettings } from '@/utils/drupal-globals'; +import { getXbSettings } from '@/utils/drupal-globals'; -const drupalSettings = getDrupalSettings(); +const xbSettings = getXbSettings(); /** * Filters out any components that are parents or children of components in the selection @@ -264,7 +264,7 @@ export function useComponentSelection() { }; // Add to Drupal settings for external access by extensions etc - drupalSettings.xb.componentSelectionUtils = componentSelectionUtils; + xbSettings.componentSelectionUtils = componentSelectionUtils; return componentSelectionUtils; } diff --git a/ui/src/hooks/useEditorNavigation.ts b/ui/src/hooks/useEditorNavigation.ts index c5ac7a62e6..e0ae3448af 100644 --- a/ui/src/hooks/useEditorNavigation.ts +++ b/ui/src/hooks/useEditorNavigation.ts @@ -1,9 +1,9 @@ import { useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { DEFAULT_REGION } from '@/features/ui/uiSlice'; -import { getBaseUrl, getDrupalSettings } from '@/utils/drupal-globals'; +import { getBaseUrl, getXbSettings } from '@/utils/drupal-globals'; -const drupalSettings = getDrupalSettings(); +const xbSettings = getXbSettings(); /** * Hook for editor navigation functions @@ -43,7 +43,7 @@ export function useEditorNavigation() { setEditorEntity, }; - drupalSettings.xb.navUtils = editorNavUtils; + xbSettings.navUtils = editorNavUtils; return editorNavUtils; } diff --git a/ui/src/utils/drupal-globals.ts b/ui/src/utils/drupal-globals.ts index 574755467f..f1dac5a208 100644 --- a/ui/src/utils/drupal-globals.ts +++ b/ui/src/utils/drupal-globals.ts @@ -1,4 +1,3 @@ -// @todo Refactor codebase to use these methods in https://drupal.org/i/3521811. const { Drupal, drupalSettings } = window as any; export const getDrupal = () => Drupal; -- GitLab From e2b235147c0696e3c5b4ae6068eef869847f09f1 Mon Sep 17 00:00:00 2001 From: Harumi Jang <harumi.jang@acquia.com> Date: Tue, 13 May 2025 18:00:07 -0400 Subject: [PATCH 5/6] Address feedback --- ui/global.d.ts | 29 +------------- ui/src/components/ComponentPreview.tsx | 5 ++- .../components/extensions/ExtensionsList.tsx | 30 +++++++------- .../form/components/drupal/DrupalTextArea.tsx | 15 +------ ui/src/features/drupal/drupalUtil.ts | 12 ------ ui/src/features/layout/layoutModelSlice.ts | 2 +- ui/src/features/layout/layoutUtils.ts | 2 +- ui/src/main.tsx | 19 ++++----- ui/src/types/DrupalSettings.ts | 39 +++++++++++++++++++ ui/src/types/FormatType.ts | 13 +++++++ ui/src/utils/drupal-globals.ts | 14 ++++++- 11 files changed, 99 insertions(+), 81 deletions(-) delete mode 100644 ui/src/features/drupal/drupalUtil.ts create mode 100644 ui/src/types/DrupalSettings.ts create mode 100644 ui/src/types/FormatType.ts diff --git a/ui/global.d.ts b/ui/global.d.ts index 331f91ce60..8f1746972f 100644 --- a/ui/global.d.ts +++ b/ui/global.d.ts @@ -1,36 +1,9 @@ -import type { PropsValues } from '@/types/Form'; import type React from 'react'; import type ReactDom from 'react-dom'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports import type * as ReactRedux from 'react-redux'; import type * as ReduxToolkit from '@reduxjs/toolkit'; - -interface DrupalSettings { - xb: { - base: string; - entityType: string; - entity: string; - entityTypeKeys: { - id: string; - label: string; - }; - globalAssets: { - css: string; - jsHeader: string; - jsFooter: string; - }; - layoutUtils: PropsValues; - componentSelectionUtils: PropsValues; - navUtils: PropsValues; - xbModulePath: string; - selectedComponent: string; - devMode: boolean; - }; - xbExtension: object; - path: { - baseUrl: string; - }; -} +import type { DrupalSettings } from '@/types/DrupalSettings'; interface CKEditor5Types { editorClassic: { diff --git a/ui/src/components/ComponentPreview.tsx b/ui/src/components/ComponentPreview.tsx index e1f38f2e3c..21ffccbefd 100644 --- a/ui/src/components/ComponentPreview.tsx +++ b/ui/src/components/ComponentPreview.tsx @@ -4,13 +4,14 @@ import styles from './ComponentPreview.module.css'; import clsx from 'clsx'; import type { XBComponent } from '@/types/Component'; import type { Section } from '@/types/Section'; -import { getDrupalSettings } from '@/utils/drupal-globals'; +import { getBaseUrl, getDrupalSettings } from '@/utils/drupal-globals'; interface ComponentPreviewProps { componentListItem: XBComponent | Section; } const drupalSettings = getDrupalSettings(); +const baseUrl = getBaseUrl(); const ComponentPreview: React.FC<ComponentPreviewProps> = ({ componentListItem, @@ -28,7 +29,7 @@ const ComponentPreview: React.FC<ComponentPreviewProps> = ({ drupalSettings?.xb.globalAssets.jsHeader + component.js_header; const markup = component.default_markup; - const base_url = window.location.origin + drupalSettings?.path.baseUrl; + const base_url = window.location.origin + baseUrl; const html = ` <html> diff --git a/ui/src/components/extensions/ExtensionsList.tsx b/ui/src/components/extensions/ExtensionsList.tsx index 9dfe17031e..6e665a763e 100644 --- a/ui/src/components/extensions/ExtensionsList.tsx +++ b/ui/src/components/extensions/ExtensionsList.tsx @@ -3,27 +3,31 @@ import { ExternalLinkIcon } from '@radix-ui/react-icons'; import ExtensionButton from '@/components/extensions/ExtensionButton'; import { handleNonWorkingBtn } from '@/utils/function-utils'; import type React from 'react'; -import { getDrupalSettings } from '@/utils/drupal-globals'; +import { + getBaseUrl, + getDrupalSettings, + getXbSettings, +} from '@/utils/drupal-globals'; interface ExtensionsPopoverProps {} const drupalSettings = getDrupalSettings(); +const baseUrl = getBaseUrl(); +const xbSettings = getXbSettings(); const ExtensionsList: React.FC<ExtensionsPopoverProps> = () => { let extensionsList = []; if (drupalSettings && drupalSettings.xbExtension) { - extensionsList = Object.values(drupalSettings.xbExtension).map( - (value: any) => { - return { - ...value, - imgSrc: - value.imgSrc || - `${drupalSettings.path.baseUrl}${drupalSettings.xb.xbModulePath}/ui/assets/icons/extension-default-abstract.svg`, - name: value.name, - description: value.description, - }; - }, - ); + extensionsList = Object.values(drupalSettings.xbExtension).map((value) => { + return { + ...value, + imgSrc: + value.imgSrc || + `${baseUrl}${xbSettings.xbModulePath}/ui/assets/icons/extension-default-abstract.svg`, + name: value.name, + description: value.description, + }; + }); } return <ExtensionsListDisplay extensions={extensionsList || []} />; diff --git a/ui/src/components/form/components/drupal/DrupalTextArea.tsx b/ui/src/components/form/components/drupal/DrupalTextArea.tsx index 7456269988..9b02c61182 100644 --- a/ui/src/components/form/components/drupal/DrupalTextArea.tsx +++ b/ui/src/components/form/components/drupal/DrupalTextArea.tsx @@ -6,6 +6,7 @@ import { Flex } from '@radix-ui/themes'; import type { Attributes } from '@/types/DrupalAttribute'; import DrupalFormattedTextArea from './DrupalFormattedTextArea'; import { getDrupalSettings } from '@/utils/drupal-globals'; +import type { FormatType } from '@/types/FormatType'; const drupalSettings = getDrupalSettings(); const DrupalTextArea = ({ @@ -68,20 +69,6 @@ const DrupalTextArea = ({ ); }; -interface FormatType { - format: string; - editor?: string; - editorSettings?: { - toolbar: any[]; - plugins: string[]; - config: { - [key: string]: any; - }; - language: Record<string, any>; - }; - [key: string]: any; -} - interface FormatSelectProps { attributes: Attributes; selectAttributes: Record<string, any>; diff --git a/ui/src/features/drupal/drupalUtil.ts b/ui/src/features/drupal/drupalUtil.ts deleted file mode 100644 index be3e7c9e3c..0000000000 --- a/ui/src/features/drupal/drupalUtil.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { PropsValues } from '@/types/Form'; -import { getDrupalSettings } from '@/utils/drupal-globals'; - -const drupalSettings = getDrupalSettings(); -export const setXbDrupalSetting = ( - property: 'layoutUtils' | 'navUtils', - value: PropsValues, -) => { - if (drupalSettings?.xb?.[property]) { - drupalSettings.xb[property] = { ...drupalSettings.xb[property], ...value }; - } -}; diff --git a/ui/src/features/layout/layoutModelSlice.ts b/ui/src/features/layout/layoutModelSlice.ts index a2e01b7b1c..dee6177f45 100644 --- a/ui/src/features/layout/layoutModelSlice.ts +++ b/ui/src/features/layout/layoutModelSlice.ts @@ -8,7 +8,7 @@ import { createSelector } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; import type { StateWithHistory } from 'redux-undo'; import { v4 as uuidv4 } from 'uuid'; -import { setXbDrupalSetting } from '@/features/drupal/drupalUtil'; +import { setXbDrupalSetting } from '@/utils/drupal-globals'; import { findComponentByUuid, findNodePathByUuid, diff --git a/ui/src/features/layout/layoutUtils.ts b/ui/src/features/layout/layoutUtils.ts index 8c4b3baf6e..fca9fd9e1c 100644 --- a/ui/src/features/layout/layoutUtils.ts +++ b/ui/src/features/layout/layoutUtils.ts @@ -8,7 +8,7 @@ import type { } from './layoutModelSlice'; import { NodeType } from './layoutModelSlice'; import { v4 as uuidv4 } from 'uuid'; -import { setXbDrupalSetting } from '@/features/drupal/drupalUtil'; +import { setXbDrupalSetting } from '@/utils/drupal-globals'; import { isConsecutive } from '@/utils/function-utils'; type NodeFunction = ( diff --git a/ui/src/main.tsx b/ui/src/main.tsx index 4fdcd80fce..00d3fbc54e 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -20,7 +20,7 @@ import transforms from '@/utils/transforms'; import '@/styles/radix-themes'; import '@/styles/index.css'; import { AJAX_UPDATE_FORM_STATE_EVENT } from '@/types/Ajax'; -import { getDrupal, getDrupalSettings } from '@/utils/drupal-globals'; +import { getBaseUrl, getDrupal, getXbSettings } from '@/utils/drupal-globals'; // Provide these dependencies as globals so extensions do not have redundant and // potentially conflicting dependencies. @@ -33,17 +33,18 @@ interface ProviderComponentProps { store: EnhancedStore; } -const drupalSettings = getDrupalSettings(); const Drupal = getDrupal(); +const xbSettings = getXbSettings(); +const baseUrl = getBaseUrl(); const container = document.getElementById('experience-builder'); const appConfiguration: AppConfiguration = { ...initialState, - baseUrl: drupalSettings?.path?.baseUrl || import.meta.env.BASE_URL, - entityType: drupalSettings?.xb?.entityType || 'node', - entity: drupalSettings?.xb?.entity || '1', - devMode: drupalSettings?.xb?.devMode || false, + baseUrl: baseUrl || import.meta.env.BASE_URL, + entityType: xbSettings.entityType || 'node', + entity: xbSettings.entity || '1', + devMode: xbSettings.devMode || false, }; const isAjaxing = () => @@ -89,13 +90,13 @@ Drupal.attachBehaviorsAfterAjaxing = attachBehaviorsAfterAjaxing; if (container) { const root = createRoot(container); let routerRoot = appConfiguration.baseUrl; - if (drupalSettings?.xb?.base) { - routerRoot = `${routerRoot}${drupalSettings.xb.base}`; + if (xbSettings.base) { + routerRoot = `${routerRoot}${xbSettings.base}`; } const store = makeStore({ configuration: appConfiguration }); // Make the store available to extensions. - (drupalSettings as any).xb.store = store; + xbSettings.store = store; root.render( <React.StrictMode> diff --git a/ui/src/types/DrupalSettings.ts b/ui/src/types/DrupalSettings.ts new file mode 100644 index 0000000000..f817332ecc --- /dev/null +++ b/ui/src/types/DrupalSettings.ts @@ -0,0 +1,39 @@ +import type { PropsValues } from '@/types/Form'; +import type { FormatType } from '@/types/FormatType'; + +export interface DrupalSettings { + xb: { + base: string; + entityType: string; + entity: string; + entityTypeKeys: { + id: string; + label: string; + }; + globalAssets: { + css: string; + jsHeader: string; + jsFooter: string; + }; + layoutUtils: PropsValues; + componentSelectionUtils: PropsValues; + navUtils: PropsValues; + xbModulePath: string; + selectedComponent: string; + devMode: boolean; + }; + xbExtension: object; + path: { + baseUrl: string; + }; + editor: { + formats: { + [key: string]: FormatType; + }; + }; + ajaxPageState: { + libraries: string; + theme: string; + theme_token: string; + }; +} diff --git a/ui/src/types/FormatType.ts b/ui/src/types/FormatType.ts new file mode 100644 index 0000000000..ab62c4c534 --- /dev/null +++ b/ui/src/types/FormatType.ts @@ -0,0 +1,13 @@ +export interface FormatType { + format: string; + editor?: string; + editorSettings?: { + toolbar: any[]; + plugins: string[]; + config: { + [key: string]: any; + }; + language: Record<string, any>; + }; + [key: string]: any; +} diff --git a/ui/src/utils/drupal-globals.ts b/ui/src/utils/drupal-globals.ts index f1dac5a208..fecd4257e9 100644 --- a/ui/src/utils/drupal-globals.ts +++ b/ui/src/utils/drupal-globals.ts @@ -1,6 +1,18 @@ +import type { PropsValues } from '@/types/Form'; +import type { DrupalSettings } from '@/types/DrupalSettings'; + const { Drupal, drupalSettings } = window as any; export const getDrupal = () => Drupal; -export const getDrupalSettings = () => drupalSettings; +export const getDrupalSettings = (): DrupalSettings => drupalSettings; export const getXbSettings = () => drupalSettings.xb; export const getBaseUrl = () => drupalSettings.path.baseUrl; + +export const setXbDrupalSetting = ( + property: 'layoutUtils' | 'navUtils', + value: PropsValues, +) => { + if (drupalSettings?.xb?.[property]) { + drupalSettings.xb[property] = { ...drupalSettings.xb[property], ...value }; + } +}; -- GitLab From 1114f705c276c983e8da89f220c40519e45ed86c Mon Sep 17 00:00:00 2001 From: Harumi Jang <harumi.jang@acquia.com> Date: Wed, 14 May 2025 10:20:42 -0400 Subject: [PATCH 6/6] Update vite mock --- ui/tests/vitest/support/vitest.setup.js | 26 +++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/ui/tests/vitest/support/vitest.setup.js b/ui/tests/vitest/support/vitest.setup.js index 08aae24d56..d9adef1345 100644 --- a/ui/tests/vitest/support/vitest.setup.js +++ b/ui/tests/vitest/support/vitest.setup.js @@ -1,5 +1,12 @@ import { vi } from 'vitest'; +const mockDrupalSettings = { + path: { + baseUrl: '/', + }, + xb: {}, +}; + vi.stubGlobal('URL', { createObjectURL: vi.fn().mockImplementation((blob) => { return `mock-object-url/${blob.name}`; @@ -10,14 +17,17 @@ vi.mock('@/utils/drupal-globals', () => ({ getDrupal: () => ({ url: (path) => `http://mock-drupal-url/${path}`, }), - getDrupalSettings: () => ({ - path: { - baseUrl: '/', - }, - xb: {}, - }), - getXbSettings: () => ({}), - getBasePath: () => '/', + getDrupalSettings: () => mockDrupalSettings, + getXbSettings: () => mockDrupalSettings.xb, + getBasePath: () => mockDrupalSettings.path.baseUrl, + setXbDrupalSetting: (property, value) => { + if (mockDrupalSettings?.xb?.[property]) { + mockDrupalSettings.xb[property] = { + ...mockDrupalSettings.xb[property], + ...value, + }; + } + }, })); vi.mock('@swc/wasm-web', () => ({ -- GitLab