diff --git a/client/src/api/admin.test.js b/client/src/api/admin.test.js index 3cd0a011fd..5b8dc9a750 100644 --- a/client/src/api/admin.test.js +++ b/client/src/api/admin.test.js @@ -1,7 +1,9 @@ -import { ADMIN_API } from '../config/wagtailConfig'; +import { WAGTAIL_CONFIG } from '../config/wagtailConfig'; import { getPageChildren, getPage } from './admin'; import client from './client'; +const { ADMIN_API } = WAGTAIL_CONFIG; + jest.mock('./client', () => { const stubResult = { __types: { diff --git a/client/src/api/admin.ts b/client/src/api/admin.ts index 1994872e32..4b076c6b62 100644 --- a/client/src/api/admin.ts +++ b/client/src/api/admin.ts @@ -1,6 +1,8 @@ import client from './client'; -import { ADMIN_API } from '../config/wagtailConfig'; +import { WAGTAIL_CONFIG } from '../config/wagtailConfig'; + +const { ADMIN_API } = WAGTAIL_CONFIG; export interface WagtailPageAPI { id: number; diff --git a/client/src/components/ChooserWidget/SnippetChooserWidget.js b/client/src/components/ChooserWidget/SnippetChooserWidget.js index 001d5c83f1..b1f2c82e81 100644 --- a/client/src/components/ChooserWidget/SnippetChooserWidget.js +++ b/client/src/components/ChooserWidget/SnippetChooserWidget.js @@ -1,17 +1,16 @@ import { ChooserModal } from '../../includes/chooserModal'; import { Chooser, ChooserFactory } from '.'; - -/* global wagtailConfig */ +import { WAGTAIL_CONFIG } from '../../config/wagtailConfig'; class SnippetChooserModal extends ChooserModal { getURLParams(opts) { const params = super.getURLParams(opts); - if (wagtailConfig.ACTIVE_CONTENT_LOCALE) { + if (WAGTAIL_CONFIG.ACTIVE_CONTENT_LOCALE) { // The user is editing a piece of translated content. // Pass the locale along as a request parameter. If this // snippet is also translatable, the results will be // pre-filtered by this locale. - params.locale = wagtailConfig.ACTIVE_CONTENT_LOCALE; + params.locale = WAGTAIL_CONFIG.ACTIVE_CONTENT_LOCALE; } return params; } diff --git a/client/src/components/PageExplorer/PageCount.tsx b/client/src/components/PageExplorer/PageCount.tsx index 23946d304b..9fc23fd462 100644 --- a/client/src/components/PageExplorer/PageCount.tsx +++ b/client/src/components/PageExplorer/PageCount.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { gettext } from '../../utils/gettext'; -import { ADMIN_URLS } from '../../config/wagtailConfig'; +import { WAGTAIL_CONFIG } from '../../config/wagtailConfig'; import Icon from '../Icon/Icon'; interface PageCountProps { @@ -18,7 +18,7 @@ const PageCount: React.FunctionComponent = ({ page }) => { return ( {gettext('See all')} diff --git a/client/src/components/PageExplorer/PageExplorerHeader.tsx b/client/src/components/PageExplorer/PageExplorerHeader.tsx index b14f6b9e36..c9a781914b 100644 --- a/client/src/components/PageExplorer/PageExplorerHeader.tsx +++ b/client/src/components/PageExplorer/PageExplorerHeader.tsx @@ -1,11 +1,13 @@ import React from 'react'; -import { ADMIN_URLS } from '../../config/wagtailConfig'; +import { WAGTAIL_CONFIG } from '../../config/wagtailConfig'; import { gettext } from '../../utils/gettext'; import Link from '../Link/Link'; import Icon from '../Icon/Icon'; import { PageState } from './reducers/nodes'; +const { ADMIN_URLS } = WAGTAIL_CONFIG; + interface SelectLocaleProps { locale?: string; translations: Map; diff --git a/client/src/components/PageExplorer/PageExplorerItem.tsx b/client/src/components/PageExplorer/PageExplorerItem.tsx index 76edc1464b..4c2b09654a 100644 --- a/client/src/components/PageExplorer/PageExplorerItem.tsx +++ b/client/src/components/PageExplorer/PageExplorerItem.tsx @@ -1,12 +1,14 @@ import React from 'react'; import { gettext } from '../../utils/gettext'; -import { ADMIN_URLS, LOCALE_NAMES } from '../../config/wagtailConfig'; +import { LOCALE_NAMES, WAGTAIL_CONFIG } from '../../config/wagtailConfig'; import Icon from '../Icon/Icon'; import Link from '../Link/Link'; import PublicationStatus from '../PublicationStatus/PublicationStatus'; import { PageState } from './reducers/nodes'; +const { ADMIN_URLS } = WAGTAIL_CONFIG; + // Hoist icons in the explorer item, as it is re-rendered many times. const childrenIcon = ; diff --git a/client/src/config/wagtailConfig.test.js b/client/src/config/wagtailConfig.test.js index dc50ce942a..b18878e8fb 100644 --- a/client/src/config/wagtailConfig.test.js +++ b/client/src/config/wagtailConfig.test.js @@ -1,32 +1,31 @@ import { - ADMIN_API, - ADMIN_URLS, + LOCALE_NAMES, MAX_EXPLORER_PAGES, WAGTAIL_CONFIG, } from './wagtailConfig'; describe('wagtailConfig', () => { - describe('ADMIN_API', () => { + describe('LOCALE_NAMES', () => { it('exists', () => { - expect(ADMIN_API).toBeDefined(); - }); - }); - - describe('ADMIN_URLS', () => { - it('exists', () => { - expect(ADMIN_URLS).toBeDefined(); + expect(LOCALE_NAMES).toBeInstanceOf(Map); + expect(LOCALE_NAMES.get('fr')).toEqual('French'); }); }); describe('MAX_EXPLORER_PAGES', () => { it('exists', () => { - expect(MAX_EXPLORER_PAGES).toBeDefined(); + expect(MAX_EXPLORER_PAGES).toBeGreaterThan(0); }); }); describe('WAGTAIL_CONFIG', () => { it('exists', () => { - expect(WAGTAIL_CONFIG).toBeDefined(); + expect(WAGTAIL_CONFIG).toEqual( + expect.objectContaining({ + ACTIVE_LOCALE: expect.any(String), + LOCALES: expect.any(Array), + }), + ); }); }); }); diff --git a/client/src/config/wagtailConfig.ts b/client/src/config/wagtailConfig.ts index db7454d1c6..fdf810d576 100644 --- a/client/src/config/wagtailConfig.ts +++ b/client/src/config/wagtailConfig.ts @@ -1,22 +1,13 @@ -export const { ADMIN_API } = global.wagtailConfig; -export const { ADMIN_URLS } = global.wagtailConfig; - -// Maximum number of pages to load inside the explorer menu. -export const MAX_EXPLORER_PAGES = 200; - -export const LOCALE_NAMES = new Map(); - -/* eslint-disable-next-line camelcase */ -global.wagtailConfig.LOCALES.forEach(({ code, display_name }) => { - LOCALE_NAMES.set(code, display_name); -}); - -function getWagtailConfig() { - // TODO: Move window.wagtailConfig from the base HTML template - // to the wagtail-config JSON script. +import type { WagtailConfig } from '../custom.d'; +const getWagtailConfig = ( + config = (global as any).wagtailConfig as WagtailConfig, +) => { + // Avoid re-parsing the JSON if global has been already created in core.js + if (config) return config; try { - return JSON.parse(document.getElementById('wagtail-config')?.textContent); + const json = document.getElementById('wagtail-config')?.textContent || ''; + return JSON.parse(json); } catch (err) { /* eslint-disable no-console */ console.error('Error loading Wagtail config'); @@ -28,6 +19,21 @@ function getWagtailConfig() { // valid JSON, ignore it and return an empty object. return {}; } -} +}; -export const WAGTAIL_CONFIG = getWagtailConfig(); +const config = getWagtailConfig() as WagtailConfig; + +/** + * Maximum number of pages to load inside the explorer menu. + */ +export const MAX_EXPLORER_PAGES = 200; + +export const LOCALE_NAMES = (config.LOCALES || []).reduce( + (locales, { code, display_name: displayName }) => { + locales.set(code, displayName); + return locales; + }, + new Map(), +); + +export { config as WAGTAIL_CONFIG }; diff --git a/client/src/custom.d.ts b/client/src/custom.d.ts index 6fcd4aa03a..bdc04a5bd8 100644 --- a/client/src/custom.d.ts +++ b/client/src/custom.d.ts @@ -1,4 +1,22 @@ -export {}; +export interface WagtailConfig { + ADMIN_API: { + PAGES: string; + DOCUMENTS: string; + IMAGES: string; + EXTRA_CHILDREN_PARAMETERS: string; + }; + ADMIN_URLS: { + DISMISSIBLES: string; + PAGES: string; + }; + CSRF_HEADER_NAME: string; + CSRF_TOKEN: string; + I18N_ENABLED: boolean; + LOCALES: { + code: string; + display_name: string; + }[]; +} declare global { interface Window { @@ -6,28 +24,5 @@ declare global { telepath: any; } - // Get text - - // Wagtail globals - - interface WagtailConfig { - ADMIN_API: { - PAGES: string; - DOCUMENTS: string; - IMAGES: string; - EXTRA_CHILDREN_PARAMETERS: string; - }; - - ADMIN_URLS: { - PAGES: string; - }; - - I18N_ENABLED: boolean; - LOCALES: { - code: string; - - display_name: string; - }[]; - } const wagtailConfig: WagtailConfig; } diff --git a/client/src/entrypoints/admin/core.js b/client/src/entrypoints/admin/core.js index 22733fbc0d..77bc306395 100644 --- a/client/src/entrypoints/admin/core.js +++ b/client/src/entrypoints/admin/core.js @@ -5,6 +5,7 @@ import { Icon, Portal } from '../..'; import { coreControllerDefinitions } from '../../controllers'; import { InlinePanel } from '../../components/InlinePanel'; import { MultipleChooserPanel } from '../../components/MultipleChooserPanel'; +import { WAGTAIL_CONFIG } from '../../config/wagtailConfig'; import { initStimulus } from '../../includes/initStimulus'; import { urlify } from '../../utils/urlify'; @@ -32,6 +33,9 @@ wagtail.app = initStimulus({ definitions: coreControllerDefinitions }); /** Expose components as globals for third-party reuse. */ wagtail.components = { Icon, Portal }; +/** Expose a global for undocumented third-party usage. */ +window.wagtailConfig = WAGTAIL_CONFIG; + window.wagtail = wagtail; window.escapeHtml = escapeHtml; diff --git a/client/tests/stubs.js b/client/tests/stubs.js index 830b5a179c..aa4c64e093 100644 --- a/client/tests/stubs.js +++ b/client/tests/stubs.js @@ -8,7 +8,7 @@ * See /wagtailadmin/templates/wagtailadmin/admin_base.html. */ -global.wagtailConfig = { +const wagtailConfig = { ADMIN_API: { DOCUMENTS: '/admin/api/main/documents/', IMAGES: '/admin/api/main/images/', @@ -18,6 +18,8 @@ global.wagtailConfig = { ADMIN_URLS: { PAGES: '/admin/pages/', }, + CSRF_HEADER_NAME: 'x-xsrf-token', + CSRF_TOKEN: 'potato', DATE_FORMATTING: { DATE_FORMAT: 'MMM. D, YYYY', SHORT_DATE_FORMAT: 'DD/MM/YYYY', @@ -36,16 +38,13 @@ global.wagtailConfig = { ACTIVE_LOCALE: 'en', }; -const script = document.createElement('script'); -script.type = 'application/json'; -script.id = 'wagtail-config'; -script.textContent = JSON.stringify({ - CSRF_HEADER_NAME: 'x-xsrf-token', - CSRF_TOKEN: 'potato', +const configScript = Object.assign(document.createElement('script'), { + id: 'wagtail-config', + textContent: JSON.stringify(wagtailConfig), + type: 'application/json', }); -document.body.appendChild(script); -global.wagtailVersion = '1.6a1'; +document.body.appendChild(configScript); global.wagtail = {}; diff --git a/wagtail/admin/templates/wagtailadmin/admin_base.html b/wagtail/admin/templates/wagtailadmin/admin_base.html index 6feb1d784d..36e8943432 100644 --- a/wagtail/admin/templates/wagtailadmin/admin_base.html +++ b/wagtail/admin/templates/wagtailadmin/admin_base.html @@ -14,34 +14,6 @@ {% endblock %} {% block js %} - {% wagtail_config as config %} {{ config|json_script:"wagtail-config" }} diff --git a/wagtail/admin/templatetags/wagtailadmin_tags.py b/wagtail/admin/templatetags/wagtailadmin_tags.py index 5c4cb7e6f6..a54c10494f 100644 --- a/wagtail/admin/templatetags/wagtailadmin_tags.py +++ b/wagtail/admin/templatetags/wagtailadmin_tags.py @@ -859,16 +859,15 @@ def i18n_enabled(): @register.simple_tag -def locales(): - return json.dumps( - [ - { - "code": locale.language_code, - "display_name": force_str(locale.get_display_name()), - } - for locale in Locale.objects.all() - ] - ) +def locales(serialize=True): + result = [ + { + "code": locale.language_code, + "display_name": force_str(locale.get_display_name()), + } + for locale in Locale.objects.all() + ] + return json.dumps(result) if serialize else result @register.simple_tag @@ -936,10 +935,25 @@ def wagtail_config(context): "CSRF_HEADER_NAME": HttpHeaders.parse_header_name( getattr(settings, "CSRF_HEADER_NAME") ), + "ADMIN_API": { + "PAGES": reverse("wagtailadmin_api:pages:listing"), + "DOCUMENTS": reverse("wagtailadmin_api:documents:listing"), + "IMAGES": reverse("wagtailadmin_api:images:listing"), + # Used to add an extra query string on all API requests. Example value: '&order=-id' + "EXTRA_CHILDREN_PARAMETERS": "", + }, "ADMIN_URLS": { "DISMISSIBLES": reverse("wagtailadmin_dismissibles"), + "PAGES": reverse("wagtailadmin_explore_root"), }, + "I18N_ENABLED": i18n_enabled(), + "LOCALES": locales(serialize=False), + "STRINGS": get_js_translation_strings(), } + + if locale := context.get("locale"): + config["ACTIVE_CONTENT_LOCALE"] = locale.language_code + return config