@elastic/eui-docusaurus-theme 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/lib/.tsbuildinfo-client +1 -1
  2. package/lib/components/demo/actions_bar/actions_bar.d.ts +2 -1
  3. package/lib/components/demo/actions_bar/actions_bar.js +2 -2
  4. package/lib/components/demo/codesandbox/open_action.js +29 -4
  5. package/lib/components/demo/demo.d.ts +2 -1
  6. package/lib/components/demo/demo.js +2 -2
  7. package/lib/components/version_switcher/index.d.ts +8 -1
  8. package/lib/components/version_switcher/index.js +37 -9
  9. package/lib/theme/Admonition/Types.js +1 -1
  10. package/lib/theme/Demo/actions.d.ts +1 -0
  11. package/lib/theme/DocBreadcrumbs/Items/Home/index.js +1 -1
  12. package/lib/theme/DocBreadcrumbs/index.js +1 -1
  13. package/lib/theme/DocCard/index.js +2 -2
  14. package/lib/theme/DocSidebarItem/Category/index.js +1 -1
  15. package/lib/theme/NavbarItem/NavbarNavLink.js +1 -1
  16. package/lib/theme/PaginatorNavLink/index.js +1 -1
  17. package/lib/theme/TOCCollapsible/CollapseButton/index.js +1 -1
  18. package/package.json +3 -3
  19. package/src/components/demo/actions_bar/actions_bar.tsx +5 -1
  20. package/src/components/demo/codesandbox/open_action.tsx +40 -4
  21. package/src/components/demo/demo.tsx +3 -0
  22. package/src/components/version_switcher/index.tsx +46 -9
  23. package/src/theme/Admonition/Types.tsx +1 -1
  24. package/src/theme/Demo/actions.tsx +1 -0
  25. package/src/theme/DocBreadcrumbs/Items/Home/index.tsx +1 -1
  26. package/src/theme/DocBreadcrumbs/index.tsx +1 -1
  27. package/src/theme/DocCard/index.tsx +2 -2
  28. package/src/theme/DocSidebarItem/Category/index.tsx +1 -1
  29. package/src/theme/NavbarItem/NavbarNavLink.tsx +1 -1
  30. package/src/theme/PaginatorNavLink/index.tsx +2 -2
  31. package/src/theme/TOCCollapsible/CollapseButton/index.tsx +1 -1
  32. package/src/theme/theme.d.ts +1 -0
@@ -3,9 +3,10 @@ export interface DemoActionsBarProps {
3
3
  activeSource: DemoSourceMeta | null;
4
4
  sources: DemoSourceMeta[];
5
5
  extraFiles?: ExtraFiles;
6
+ previewWrapperSource?: string;
6
7
  isSourceOpen: boolean;
7
8
  setSourceOpen(isOpen: boolean): void;
8
9
  onClickReloadExample(): void;
9
10
  onClickCopyToClipboard(): void;
10
11
  }
11
- export declare const DemoActionsBar: ({ isSourceOpen, setSourceOpen, activeSource, sources, extraFiles, onClickReloadExample, onClickCopyToClipboard, }: DemoActionsBarProps) => import("@emotion/react/jsx-runtime").JSX.Element;
12
+ export declare const DemoActionsBar: ({ isSourceOpen, setSourceOpen, activeSource, sources, extraFiles, previewWrapperSource, onClickReloadExample, onClickCopyToClipboard, }: DemoActionsBarProps) => import("@emotion/react/jsx-runtime").JSX.Element;
@@ -28,7 +28,7 @@ const getDemoActionsBarStyles = (euiTheme) => {
28
28
  `,
29
29
  };
30
30
  };
31
- export const DemoActionsBar = ({ isSourceOpen, setSourceOpen, activeSource, sources, extraFiles, onClickReloadExample, onClickCopyToClipboard, }) => {
31
+ export const DemoActionsBar = ({ isSourceOpen, setSourceOpen, activeSource, sources, extraFiles, previewWrapperSource, onClickReloadExample, onClickCopyToClipboard, }) => {
32
32
  const styles = useEuiMemoizedStyles(getDemoActionsBarStyles);
33
- return (_jsxs(EuiFlexGroup, { alignItems: "center", css: styles.actionsBar, gutterSize: "s", children: [_jsx(EuiButton, { css: styles.button, onClick: () => setSourceOpen(!isSourceOpen), size: "s", color: "text", minWidth: false, children: isSourceOpen ? 'Hide source' : 'Show source' }), extraActions.map((ActionComponent) => (_jsx(ActionComponent, { sources: sources, extraFiles: extraFiles, activeSource: activeSource }))), _jsx(EuiToolTip, { content: "Copy to clipboard", children: _jsx(EuiButtonIcon, { size: "s", iconType: "copyClipboard", color: "text", onClick: onClickCopyToClipboard, "aria-label": "Copy code to clipboard" }) }), _jsx(EuiToolTip, { content: "Reload example", children: _jsx(EuiButtonIcon, { size: "s", iconType: "refresh", color: "text", onClick: onClickReloadExample, "aria-label": "Reload example" }) })] }));
33
+ return (_jsxs(EuiFlexGroup, { alignItems: "center", css: styles.actionsBar, gutterSize: "s", children: [_jsx(EuiButton, { css: styles.button, onClick: () => setSourceOpen(!isSourceOpen), size: "s", color: "text", minWidth: false, children: isSourceOpen ? 'Hide source' : 'Show source' }), extraActions.map((ActionComponent) => (_jsx(ActionComponent, { sources: sources, extraFiles: extraFiles, previewWrapperSource: previewWrapperSource, activeSource: activeSource }, ActionComponent.displayName ?? ActionComponent.name))), _jsx(EuiToolTip, { content: "Copy to clipboard", children: _jsx(EuiButtonIcon, { size: "s", iconType: "copy", color: "text", onClick: onClickCopyToClipboard, "aria-label": "Copy code to clipboard" }) }), _jsx(EuiToolTip, { content: "Reload example", children: _jsx(EuiButtonIcon, { size: "s", iconType: "refresh", color: "text", onClick: onClickReloadExample, "aria-label": "Reload example" }) })] }));
34
34
  };
@@ -32,7 +32,8 @@ const indexTsxSource = dedent `
32
32
  <EuiProvider cache={cache}>
33
33
  <Demo />
34
34
  </EuiProvider>
35
- );`;
35
+ );
36
+ `;
36
37
  const publicIndexHtmlSource = dedent `
37
38
  <head>
38
39
  <title>@elastic/eui component demo</title>
@@ -59,9 +60,21 @@ export const defaultCodeSandboxParameters = {
59
60
  const processTsxSource = (source) => {
60
61
  // jsxImportSource pragma is needed in CodeSandbox as it doesn't seem
61
62
  // to support that setting via tsconfig.json
62
- return `/** @jsxImportSource @emotion/react */\n${source}`;
63
+ let processed = `/** @jsxImportSource @emotion/react */\n${source}`;
64
+ // Inject `@elastic/charts` CSS for demos that use the charts library,
65
+ // since CodeSandbox doesn't load it globally unlike the docs site
66
+ if (source.includes('@elastic/charts')) {
67
+ // Match the closing line of the @elastic/charts import (handles both
68
+ // single-line and multi-line imports, and both quote styles)
69
+ processed = processed.replace(/^(.*from ['"]@elastic\/charts['"];)$/m, [
70
+ '$1',
71
+ "import '@elastic/charts/dist/theme_only_light.css';",
72
+ "import '@elastic/charts/dist/theme_only_dark.css';",
73
+ ].join('\n'));
74
+ }
75
+ return processed;
63
76
  };
64
- export const createOpenInCodeSandboxAction = ({ files = {}, dependencies }) => ({ extraFiles, activeSource }) => {
77
+ export const createOpenInCodeSandboxAction = ({ files = {}, dependencies }) => ({ extraFiles, activeSource, previewWrapperSource }) => {
65
78
  const parameters = useMemo(() => {
66
79
  const source = activeSource?.code || '';
67
80
  // Compute list of extra files that may be passed
@@ -75,6 +88,18 @@ export const createOpenInCodeSandboxAction = ({ files = {}, dependencies }) => (
75
88
  return getParameters({
76
89
  files: {
77
90
  ...defaultCodeSandboxParameters.files,
91
+ ...(previewWrapperSource
92
+ ? {
93
+ 'index.tsx': {
94
+ content: indexTsxSource
95
+ .replace("import Demo from './demo';", "import Demo from './demo';\nimport PreviewWrapper from './preview_wrapper';")
96
+ .replace('<Demo />', '<PreviewWrapper>\n <Demo />\n </PreviewWrapper>'),
97
+ },
98
+ 'preview_wrapper.tsx': {
99
+ content: processTsxSource(previewWrapperSource),
100
+ },
101
+ }
102
+ : {}),
78
103
  'demo.tsx': {
79
104
  content: processTsxSource(source),
80
105
  },
@@ -89,6 +114,6 @@ export const createOpenInCodeSandboxAction = ({ files = {}, dependencies }) => (
89
114
  ...codeSandboxFiles,
90
115
  },
91
116
  });
92
- }, [activeSource, extraFiles]);
117
+ }, [activeSource, extraFiles, previewWrapperSource]);
93
118
  return (_jsxs("form", { action: "https://codesandbox.io/api/v1/sandboxes/define", method: "POST", target: "_blank", children: [_jsx("input", { type: "hidden", name: "parameters", value: parameters }), _jsx("input", { type: "hidden", name: "query", value: `module=/demo.tsx&view=split` }), _jsx(EuiToolTip, { content: "Open in CodeSandbox", children: _jsx(EuiButtonIcon, { type: "submit", size: "s", iconType: CodeSandboxIcon, color: "text", "aria-label": "Open in CodeSandbox" }) })] }));
94
119
  };
@@ -45,5 +45,6 @@ export interface DemoProps extends PropsWithChildren {
45
45
  extraFiles?: ExtraFiles;
46
46
  previewPadding?: DemoPreviewProps['padding'];
47
47
  previewWrapper?: DemoPreviewProps['wrapperComponent'];
48
+ previewWrapperSource?: string;
48
49
  }
49
- export declare const Demo: ({ children, scope, extraFiles, isSourceOpen: _isSourceOpen, previewPadding, previewWrapper, }: DemoProps) => import("@emotion/react/jsx-runtime").JSX.Element;
50
+ export declare const Demo: ({ children, scope, extraFiles, isSourceOpen: _isSourceOpen, previewPadding, previewWrapper, previewWrapperSource, }: DemoProps) => import("@emotion/react/jsx-runtime").JSX.Element;
@@ -31,7 +31,7 @@ const getDemoStyles = (euiTheme) => ({
31
31
  word-break: break-word;
32
32
  `,
33
33
  });
34
- export const Demo = ({ children, scope, extraFiles, isSourceOpen: _isSourceOpen = false, previewPadding, previewWrapper, }) => {
34
+ export const Demo = ({ children, scope, extraFiles, isSourceOpen: _isSourceOpen = false, previewPadding, previewWrapper, previewWrapperSource, }) => {
35
35
  const styles = useEuiMemoizedStyles(getDemoStyles);
36
36
  const [sources, setSources] = useState([]);
37
37
  const [isSourceOpen, setIsSourceOpen] = useState(_isSourceOpen);
@@ -52,7 +52,7 @@ export const Demo = ({ children, scope, extraFiles, isSourceOpen: _isSourceOpen
52
52
  const onClickReloadExample = useCallback(() => {
53
53
  setLiveProviderKey((liveProviderKey) => liveProviderKey + 1);
54
54
  }, []);
55
- return (_jsx("div", { css: styles.demo, children: _jsxs(DemoContext.Provider, { value: { sources, addSource }, children: [_jsxs(LiveProvider, { code: activeSource?.code || '', transformCode: demoCodeTransformer, theme: prismThemes.dracula, scope: finalScope, children: [_jsx(DemoPreview, { padding: previewPadding, wrapperComponent: previewWrapper }), _jsx(DemoActionsBar, { isSourceOpen: isSourceOpen, setSourceOpen: setIsSourceOpen, activeSource: activeSource, extraFiles: extraFiles, sources: sources, onClickCopyToClipboard: onClickCopyToClipboard, onClickReloadExample: onClickReloadExample }), isSourceOpen && _jsx(DemoEditor, {})] }, liveProviderKey), Children.map(children, (child, index) => {
55
+ return (_jsx("div", { css: styles.demo, children: _jsxs(DemoContext.Provider, { value: { sources, addSource }, children: [_jsxs(LiveProvider, { code: activeSource?.code || '', transformCode: demoCodeTransformer, theme: prismThemes.dracula, scope: finalScope, children: [_jsx(DemoPreview, { padding: previewPadding, wrapperComponent: previewWrapper }), _jsx(DemoActionsBar, { isSourceOpen: isSourceOpen, setSourceOpen: setIsSourceOpen, activeSource: activeSource, extraFiles: extraFiles, sources: sources, previewWrapperSource: previewWrapperSource, onClickCopyToClipboard: onClickCopyToClipboard, onClickReloadExample: onClickReloadExample }), isSourceOpen && _jsx(DemoEditor, {})] }, liveProviderKey), Children.map(children, (child, index) => {
56
56
  if (isElement(child) && child.type === DemoSource) {
57
57
  return child;
58
58
  }
@@ -19,5 +19,12 @@ export type VersionSwitcherProps = {
19
19
  * List of available versions to switch to
20
20
  */
21
21
  versions: string[];
22
+ /**
23
+ * URL to fetch the latest list of versions from.
24
+ * When provided, versions are fetched client-side from this URL,
25
+ * falling back to the static `versions` prop if the fetch fails.
26
+ * This ensures all deployed versions are always visible in the switcher.
27
+ */
28
+ versionsUrl?: string;
22
29
  };
23
- export declare const VersionSwitcher: ({ ariaLabel, currentVersion, extraAction, previousVersionUrl, versions, }: VersionSwitcherProps) => import("@emotion/react/jsx-runtime").JSX.Element | null;
30
+ export declare const VersionSwitcher: ({ ariaLabel, currentVersion, extraAction, previousVersionUrl, versions, versionsUrl, }: VersionSwitcherProps) => import("@emotion/react/jsx-runtime").JSX.Element | null;
@@ -6,7 +6,7 @@ import { jsxs as _jsxs, jsx as _jsx } from "@emotion/react/jsx-runtime";
6
6
  * in compliance with, at your election, the Elastic License 2.0 or the Server
7
7
  * Side Public License, v 1.
8
8
  */
9
- import { useState } from 'react';
9
+ import { useEffect, useState } from 'react';
10
10
  import { css } from '@emotion/react';
11
11
  import { FixedSizeList } from 'react-window';
12
12
  import { EuiButtonEmpty, euiFocusRing, EuiListGroupItem, EuiPopover, useEuiMemoizedStyles, } from '@elastic/eui';
@@ -33,19 +33,47 @@ const getStyles = (euiThemeContext) => {
33
33
  const pronounceVersion = (version) => {
34
34
  return `version ${version.replaceAll('.', ' point ')}`;
35
35
  };
36
- export const VersionSwitcher = ({ ariaLabel, currentVersion, extraAction, previousVersionUrl, versions, }) => {
36
+ export const VersionSwitcher = ({ ariaLabel, currentVersion, extraAction, previousVersionUrl, versions, versionsUrl, }) => {
37
37
  const [isPopoverOpen, setPopoverOpen] = useState(false);
38
+ const [allVersions, setAllVersions] = useState(versions);
38
39
  const styles = useEuiMemoizedStyles(getStyles);
39
- if (!versions)
40
+ // Fetch the latest versions list from the main deployment
41
+ // to ensure the switcher always shows all available versions,
42
+ // even on older documentation deployments
43
+ useEffect(() => {
44
+ if (!versionsUrl)
45
+ return;
46
+ fetch(versionsUrl)
47
+ .then((response) => {
48
+ if (!response.ok)
49
+ throw new Error('Failed to fetch versions');
50
+ return response.json();
51
+ })
52
+ .then((data) => {
53
+ if (data?.euiVersions?.length) {
54
+ // Ensure the current version is always included
55
+ const fetchedVersions = data.euiVersions;
56
+ if (!fetchedVersions.includes(currentVersion)) {
57
+ fetchedVersions.push(currentVersion);
58
+ }
59
+ setAllVersions(fetchedVersions);
60
+ }
61
+ })
62
+ .catch(() => {
63
+ // Silently fall back to statically bundled versions
64
+ });
65
+ }, [versionsUrl, currentVersion]);
66
+ if (!allVersions?.length)
40
67
  return null;
41
- const button = (_jsxs(EuiButtonEmpty, { size: "s", color: "text", iconType: "arrowDown", iconSide: "right", css: styles.button, onClick: () => setPopoverOpen((isOpen) => !isOpen), "aria-label": `${pronounceVersion(currentVersion)}. Click to switch versions`, children: ["v", currentVersion] }));
42
- return (_jsx(EuiPopover, { isOpen: isPopoverOpen, closePopover: () => setPopoverOpen(false), button: button, repositionOnScroll: true, panelPaddingSize: "xs", "aria-label": ariaLabel, children: _jsx(FixedSizeList, { className: "eui-yScroll", itemCount: versions.length, itemSize: 24, height: 200, width: 120, innerElementType: "ul", children: ({ index, style }) => {
43
- const version = versions[index];
68
+ const latestVersion = allVersions[0];
69
+ const button = (_jsxs(EuiButtonEmpty, { size: "s", color: "text", iconType: "chevronSingleDown", iconSide: "right", css: styles.button, onClick: () => setPopoverOpen((isOpen) => !isOpen), "aria-label": `${pronounceVersion(currentVersion)}. Click to switch versions`, children: ["v", currentVersion] }));
70
+ return (_jsx(EuiPopover, { isOpen: isPopoverOpen, closePopover: () => setPopoverOpen(false), button: button, repositionOnScroll: true, panelPaddingSize: "xs", "aria-label": ariaLabel, children: _jsx(FixedSizeList, { className: "eui-yScroll", itemCount: allVersions.length, itemSize: 24, height: 200, width: 120, innerElementType: "ul", children: ({ index, style }) => {
71
+ const version = allVersions[index];
44
72
  const isCurrentVersion = version === currentVersion;
45
73
  const screenReaderVersion = pronounceVersion(version);
46
- const url = isCurrentVersion
47
- ? '/'
74
+ const url = version === latestVersion
75
+ ? `${previousVersionUrl}/`
48
76
  : `${previousVersionUrl}/v${version}/`;
49
- return (_jsx(EuiListGroupItem, { css: styles.listItem, style: style, size: "xs", label: `v${version}`, "aria-label": screenReaderVersion, href: url, isActive: isCurrentVersion, color: isCurrentVersion ? 'primary' : 'text', extraAction: extraAction?.(version) }));
77
+ return (_jsx(EuiListGroupItem, { css: styles.listItem, style: style, label: `v${version}`, "aria-label": screenReaderVersion, href: url, isActive: isCurrentVersion, color: isCurrentVersion ? 'primary' : 'text', extraAction: extraAction?.(version) }));
50
78
  } }) }));
51
79
  };
@@ -13,7 +13,7 @@ const VARIANT_TO_PROPS_MAP = {
13
13
  tip: { iconType: 'faceHappy', color: 'success' },
14
14
  info: { iconType: 'info', color: 'primary' },
15
15
  accessibility: { iconType: 'accessibility', color: 'primary' },
16
- warning: { iconType: 'alert', color: 'warning' },
16
+ warning: { iconType: 'warning', color: 'warning' },
17
17
  danger: { iconType: 'error', color: 'danger' },
18
18
  // Undocumented legacy admonition type aliases
19
19
  secondary: {},
@@ -4,6 +4,7 @@ export type ActionComponentProps = {
4
4
  activeSource: DemoSourceMeta | null;
5
5
  sources: DemoSourceMeta[];
6
6
  extraFiles?: ExtraFiles;
7
+ previewWrapperSource?: string;
7
8
  };
8
9
  export type ActionComponent = ComponentType<ActionComponentProps>;
9
10
  export declare const extraActions: ActionComponent[];
@@ -14,5 +14,5 @@ export default function HomeBreadcrumbItem() {
14
14
  id: 'theme.docs.breadcrumbs.home',
15
15
  message: 'Home page',
16
16
  description: 'The ARIA label for the home page in the breadcrumbs',
17
- }), className: "breadcrumbs__link", href: homeHref, children: title }), _jsx(EuiIcon, { type: "arrowRight", size: "s", css: styles.icon })] }));
17
+ }), className: "breadcrumbs__link", href: homeHref, children: title }), _jsx(EuiIcon, { type: "chevronSingleRight", size: "s", css: styles.icon })] }));
18
18
  }
@@ -40,7 +40,7 @@ function BreadcrumbsItem({ children, active, index, addMicrodata, }) {
40
40
  itemType: 'https://schema.org/ListItem',
41
41
  }), className: clsx('breadcrumbs__item', {
42
42
  'breadcrumbs__item--active': active,
43
- }), css: styles.item, children: [children, _jsx("meta", { itemProp: "position", content: String(index + 1) }), !active && _jsx(EuiIcon, { type: "arrowRight", css: styles.icon })] }));
43
+ }), css: styles.item, children: [children, _jsx("meta", { itemProp: "position", content: String(index + 1) }), !active && _jsx(EuiIcon, { type: "chevronSingleRight", css: styles.icon })] }));
44
44
  }
45
45
  export default function DocBreadcrumbs() {
46
46
  const breadcrumbs = useSidebarBreadcrumbs();
@@ -27,8 +27,8 @@ function CardCategory({ item }) {
27
27
  icon: "folderOpen", title: item.label, description: item.description ?? categoryItemsPlural(item.items.length) }));
28
28
  }
29
29
  function CardLink({ item }) {
30
- // We update Docusaurus `link` icon to EUI `popout` icon
31
- const icon = isInternalUrl(item.href) ? 'document' : 'popout';
30
+ // We update Docusaurus `link` icon to EUI `external` icon
31
+ const icon = isInternalUrl(item.href) ? 'document' : 'external';
32
32
  const doc = useDocById(item.docId ?? undefined);
33
33
  return (_jsx(CardLayout, { href: item.href, icon: icon, title: item.label, description: item.description ?? doc?.description }));
34
34
  }
@@ -85,7 +85,7 @@ function CollapseButton({ collapsed, categoryLabel, onClick, ...rest }) {
85
85
  id: 'theme.DocSidebarItem.collapseCategoryAriaLabel',
86
86
  message: "Collapse sidebar category '{label}'",
87
87
  description: 'The ARIA label to collapse the sidebar category',
88
- }, { label: categoryLabel }), "aria-expanded": !collapsed, type: "button", className: "clean-btn", onClick: onClick, ...rest, children: _jsx(EuiIcon, { size: "s", type: collapsed ? 'arrowRight' : 'arrowDown' }) }));
88
+ }, { label: categoryLabel }), "aria-expanded": !collapsed, type: "button", className: "clean-btn", onClick: onClick, ...rest, children: _jsx(EuiIcon, { size: "s", type: collapsed ? 'chevronSingleRight' : 'chevronSingleDown' }) }));
89
89
  }
90
90
  export default function DocSidebarItemCategory({ item, onItemClick, activePath, level, index, ...props }) {
91
91
  const { items, label, collapsible, className, href } = item;
@@ -12,7 +12,7 @@ const CUSTOM_LINK_COMPONENT_MAP = {
12
12
  },
13
13
  changelog: {
14
14
  component: NavbarItem,
15
- icon: 'cheer',
15
+ icon: 'popper',
16
16
  },
17
17
  figma: {
18
18
  component: NavbarItem,
@@ -39,5 +39,5 @@ export default function PaginatorNavLink(props) {
39
39
  const { permalink, title, subLabel, isNext } = props;
40
40
  const isPrev = !isNext;
41
41
  const styles = useEuiMemoizedStyles(getStyles);
42
- return (_jsxs(Link, { className: clsx('pagination-nav__link', isNext ? 'pagination-nav__link--next' : 'pagination-nav__link--prev'), css: styles.link, to: permalink, children: [subLabel && _jsx("div", { className: "pagination-nav__sublabel", children: subLabel }), _jsxs("div", { className: "pagination-nav__label", children: [isPrev && _jsx(EuiIcon, { type: "arrowLeft" }), title, isNext && _jsx(EuiIcon, { type: "arrowRight" })] })] }));
42
+ return (_jsxs(Link, { className: clsx('pagination-nav__link', isNext ? 'pagination-nav__link--next' : 'pagination-nav__link--prev'), css: styles.link, to: permalink, children: [subLabel && _jsx("div", { className: "pagination-nav__sublabel", children: subLabel }), _jsxs("div", { className: "pagination-nav__label", children: [isPrev && _jsx(EuiIcon, { type: "chevronSingleLeft" }), title, isNext && _jsx(EuiIcon, { type: "chevronSingleRight" })] })] }));
43
43
  }
@@ -19,5 +19,5 @@ const getStyles = ({ euiTheme }) => {
19
19
  };
20
20
  export default function TOCCollapsibleCollapseButton({ collapsed, ...props }) {
21
21
  const styles = useEuiMemoizedStyles(getStyles);
22
- return (_jsxs("button", { type: "button", ...props, className: clsx('clean-btn', props.className), css: styles.tocCollapsibleButton, children: [_jsx(Translate, { id: "theme.TOCCollapsible.toggleButtonLabel", description: "The label used by the button on the collapsible TOC component", children: "On this page" }), _jsx(EuiIcon, { type: collapsed ? 'arrowDown' : 'arrowUp' })] }));
22
+ return (_jsxs("button", { type: "button", ...props, className: clsx('clean-btn', props.className), css: styles.tocCollapsibleButton, children: [_jsx(Translate, { id: "theme.TOCCollapsible.toggleButtonLabel", description: "The label used by the button on the collapsible TOC component", children: "On this page" }), _jsx(EuiIcon, { type: collapsed ? 'chevronSingleDown' : 'chevronSingleUp' })] }));
23
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elastic/eui-docusaurus-theme",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "EUI theme for Docusaurus",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "scripts": {
@@ -40,8 +40,8 @@
40
40
  "@docusaurus/theme-common": "^3.7.0",
41
41
  "@docusaurus/utils-validation": "^3.7.0",
42
42
  "@elastic/datemath": "^5.0.3",
43
- "@elastic/eui": "^112.1.0",
44
- "@elastic/eui-theme-borealis": "^5.4.0",
43
+ "@elastic/eui": "^116.0.0",
44
+ "@elastic/eui-theme-borealis": "^8.0.0",
45
45
  "@emotion/css": "^11.11.2",
46
46
  "@emotion/react": "^11.11.4",
47
47
  "@types/react-window": "^1.8.8",
@@ -23,6 +23,7 @@ export interface DemoActionsBarProps {
23
23
  activeSource: DemoSourceMeta | null;
24
24
  sources: DemoSourceMeta[];
25
25
  extraFiles?: ExtraFiles;
26
+ previewWrapperSource?: string;
26
27
  isSourceOpen: boolean;
27
28
  setSourceOpen(isOpen: boolean): void;
28
29
  onClickReloadExample(): void;
@@ -55,6 +56,7 @@ export const DemoActionsBar = ({
55
56
  activeSource,
56
57
  sources,
57
58
  extraFiles,
59
+ previewWrapperSource,
58
60
  onClickReloadExample,
59
61
  onClickCopyToClipboard,
60
62
  }: DemoActionsBarProps) => {
@@ -73,15 +75,17 @@ export const DemoActionsBar = ({
73
75
  </EuiButton>
74
76
  {extraActions.map((ActionComponent) => (
75
77
  <ActionComponent
78
+ key={ActionComponent.displayName ?? ActionComponent.name}
76
79
  sources={sources}
77
80
  extraFiles={extraFiles}
81
+ previewWrapperSource={previewWrapperSource}
78
82
  activeSource={activeSource}
79
83
  />
80
84
  ))}
81
85
  <EuiToolTip content="Copy to clipboard">
82
86
  <EuiButtonIcon
83
87
  size="s"
84
- iconType="copyClipboard"
88
+ iconType="copy"
85
89
  color="text"
86
90
  onClick={onClickCopyToClipboard}
87
91
  aria-label="Copy code to clipboard"
@@ -34,7 +34,8 @@ const indexTsxSource = dedent`
34
34
  <EuiProvider cache={cache}>
35
35
  <Demo />
36
36
  </EuiProvider>
37
- );`;
37
+ );
38
+ `;
38
39
 
39
40
  const publicIndexHtmlSource = dedent`
40
41
  <head>
@@ -69,12 +70,29 @@ export type Options = {
69
70
  const processTsxSource = (source: string) => {
70
71
  // jsxImportSource pragma is needed in CodeSandbox as it doesn't seem
71
72
  // to support that setting via tsconfig.json
72
- return `/** @jsxImportSource @emotion/react */\n${source}`;
73
+ let processed = `/** @jsxImportSource @emotion/react */\n${source}`;
74
+
75
+ // Inject `@elastic/charts` CSS for demos that use the charts library,
76
+ // since CodeSandbox doesn't load it globally unlike the docs site
77
+ if (source.includes('@elastic/charts')) {
78
+ // Match the closing line of the @elastic/charts import (handles both
79
+ // single-line and multi-line imports, and both quote styles)
80
+ processed = processed.replace(
81
+ /^(.*from ['"]@elastic\/charts['"];)$/m,
82
+ [
83
+ '$1',
84
+ "import '@elastic/charts/dist/theme_only_light.css';",
85
+ "import '@elastic/charts/dist/theme_only_dark.css';",
86
+ ].join('\n')
87
+ );
88
+ }
89
+
90
+ return processed;
73
91
  };
74
92
 
75
93
  export const createOpenInCodeSandboxAction =
76
94
  ({ files = {}, dependencies }: Options): ActionComponent =>
77
- ({ extraFiles, activeSource }) => {
95
+ ({ extraFiles, activeSource, previewWrapperSource }) => {
78
96
  const parameters: string = useMemo(() => {
79
97
  const source = activeSource?.code || '';
80
98
 
@@ -93,6 +111,24 @@ export const createOpenInCodeSandboxAction =
93
111
  return getParameters({
94
112
  files: {
95
113
  ...defaultCodeSandboxParameters.files,
114
+ ...(previewWrapperSource
115
+ ? {
116
+ 'index.tsx': {
117
+ content: indexTsxSource
118
+ .replace(
119
+ "import Demo from './demo';",
120
+ "import Demo from './demo';\nimport PreviewWrapper from './preview_wrapper';"
121
+ )
122
+ .replace(
123
+ '<Demo />',
124
+ '<PreviewWrapper>\n <Demo />\n </PreviewWrapper>'
125
+ ),
126
+ },
127
+ 'preview_wrapper.tsx': {
128
+ content: processTsxSource(previewWrapperSource),
129
+ },
130
+ }
131
+ : {}),
96
132
  'demo.tsx': {
97
133
  content: processTsxSource(source),
98
134
  },
@@ -107,7 +143,7 @@ export const createOpenInCodeSandboxAction =
107
143
  ...codeSandboxFiles,
108
144
  },
109
145
  } as any);
110
- }, [activeSource, extraFiles]);
146
+ }, [activeSource, extraFiles, previewWrapperSource]);
111
147
 
112
148
  return (
113
149
  <form
@@ -79,6 +79,7 @@ export interface DemoProps extends PropsWithChildren {
79
79
  extraFiles?: ExtraFiles;
80
80
  previewPadding?: DemoPreviewProps['padding'];
81
81
  previewWrapper?: DemoPreviewProps['wrapperComponent'];
82
+ previewWrapperSource?: string;
82
83
  }
83
84
 
84
85
  const getDemoStyles = (euiTheme: UseEuiTheme) => ({
@@ -100,6 +101,7 @@ export const Demo = ({
100
101
  isSourceOpen: _isSourceOpen = false,
101
102
  previewPadding,
102
103
  previewWrapper,
104
+ previewWrapperSource,
103
105
  }: DemoProps) => {
104
106
  const styles = useEuiMemoizedStyles(getDemoStyles);
105
107
  const [sources, setSources] = useState<DemoSourceMeta[]>([]);
@@ -153,6 +155,7 @@ export const Demo = ({
153
155
  activeSource={activeSource}
154
156
  extraFiles={extraFiles}
155
157
  sources={sources}
158
+ previewWrapperSource={previewWrapperSource}
156
159
  onClickCopyToClipboard={onClickCopyToClipboard}
157
160
  onClickReloadExample={onClickReloadExample}
158
161
  />
@@ -6,7 +6,7 @@
6
6
  * Side Public License, v 1.
7
7
  */
8
8
 
9
- import { ComponentProps, useState } from 'react';
9
+ import { ComponentProps, useEffect, useState } from 'react';
10
10
  import { css } from '@emotion/react';
11
11
  import { FixedSizeList } from 'react-window';
12
12
  import {
@@ -68,6 +68,13 @@ export type VersionSwitcherProps = {
68
68
  * List of available versions to switch to
69
69
  */
70
70
  versions: string[];
71
+ /**
72
+ * URL to fetch the latest list of versions from.
73
+ * When provided, versions are fetched client-side from this URL,
74
+ * falling back to the static `versions` prop if the fetch fails.
75
+ * This ensures all deployed versions are always visible in the switcher.
76
+ */
77
+ versionsUrl?: string;
71
78
  };
72
79
 
73
80
  export const VersionSwitcher = ({
@@ -76,17 +83,47 @@ export const VersionSwitcher = ({
76
83
  extraAction,
77
84
  previousVersionUrl,
78
85
  versions,
86
+ versionsUrl,
79
87
  }: VersionSwitcherProps) => {
80
88
  const [isPopoverOpen, setPopoverOpen] = useState(false);
89
+ const [allVersions, setAllVersions] = useState(versions);
81
90
  const styles = useEuiMemoizedStyles(getStyles);
82
91
 
83
- if (!versions) return null;
92
+ // Fetch the latest versions list from the main deployment
93
+ // to ensure the switcher always shows all available versions,
94
+ // even on older documentation deployments
95
+ useEffect(() => {
96
+ if (!versionsUrl) return;
97
+
98
+ fetch(versionsUrl)
99
+ .then((response) => {
100
+ if (!response.ok) throw new Error('Failed to fetch versions');
101
+ return response.json();
102
+ })
103
+ .then((data) => {
104
+ if (data?.euiVersions?.length) {
105
+ // Ensure the current version is always included
106
+ const fetchedVersions: string[] = data.euiVersions;
107
+ if (!fetchedVersions.includes(currentVersion)) {
108
+ fetchedVersions.push(currentVersion);
109
+ }
110
+ setAllVersions(fetchedVersions);
111
+ }
112
+ })
113
+ .catch(() => {
114
+ // Silently fall back to statically bundled versions
115
+ });
116
+ }, [versionsUrl, currentVersion]);
117
+
118
+ if (!allVersions?.length) return null;
119
+
120
+ const latestVersion = allVersions[0];
84
121
 
85
122
  const button = (
86
123
  <EuiButtonEmpty
87
124
  size="s"
88
125
  color="text"
89
- iconType="arrowDown"
126
+ iconType="chevronSingleDown"
90
127
  iconSide="right"
91
128
  css={styles.button}
92
129
  onClick={() => setPopoverOpen((isOpen) => !isOpen)}
@@ -109,26 +146,26 @@ export const VersionSwitcher = ({
109
146
  >
110
147
  <FixedSizeList
111
148
  className="eui-yScroll"
112
- itemCount={versions.length}
149
+ itemCount={allVersions.length}
113
150
  itemSize={24}
114
151
  height={200}
115
152
  width={120}
116
153
  innerElementType="ul"
117
154
  >
118
155
  {({ index, style }) => {
119
- const version = versions[index];
156
+ const version = allVersions[index];
120
157
  const isCurrentVersion = version === currentVersion;
121
158
  const screenReaderVersion = pronounceVersion(version!);
122
159
 
123
- const url = isCurrentVersion
124
- ? '/'
125
- : `${previousVersionUrl}/v${version}/`;
160
+ const url =
161
+ version === latestVersion
162
+ ? `${previousVersionUrl}/`
163
+ : `${previousVersionUrl}/v${version}/`;
126
164
 
127
165
  return (
128
166
  <EuiListGroupItem
129
167
  css={styles.listItem}
130
168
  style={style}
131
- size="xs"
132
169
  label={`v${version}`}
133
170
  aria-label={screenReaderVersion}
134
171
  href={url}
@@ -32,7 +32,7 @@ const VARIANT_TO_PROPS_MAP: Record<
32
32
  tip: { iconType: 'faceHappy', color: 'success' },
33
33
  info: { iconType: 'info', color: 'primary' },
34
34
  accessibility: { iconType: 'accessibility', color: 'primary' },
35
- warning: { iconType: 'alert', color: 'warning' },
35
+ warning: { iconType: 'warning', color: 'warning' },
36
36
  danger: { iconType: 'error', color: 'danger' },
37
37
  // Undocumented legacy admonition type aliases
38
38
  secondary: {},
@@ -14,6 +14,7 @@ export type ActionComponentProps = {
14
14
  activeSource: DemoSourceMeta | null;
15
15
  sources: DemoSourceMeta[];
16
16
  extraFiles?: ExtraFiles;
17
+ previewWrapperSource?: string;
17
18
  };
18
19
 
19
20
  export type ActionComponent = ComponentType<ActionComponentProps>;
@@ -35,7 +35,7 @@ export default function HomeBreadcrumbItem(): JSX.Element {
35
35
  >
36
36
  {title}
37
37
  </Link>
38
- <EuiIcon type="arrowRight" size="s" css={styles.icon} />
38
+ <EuiIcon type="chevronSingleRight" size="s" css={styles.icon} />
39
39
  </li>
40
40
  );
41
41
  }
@@ -88,7 +88,7 @@ function BreadcrumbsItem({
88
88
  >
89
89
  {children}
90
90
  <meta itemProp="position" content={String(index + 1)} />
91
- {!active && <EuiIcon type="arrowRight" css={styles.icon} />}
91
+ {!active && <EuiIcon type="chevronSingleRight" css={styles.icon} />}
92
92
  </li>
93
93
  );
94
94
  }
@@ -75,8 +75,8 @@ function CardCategory({ item }: { item: PropSidebarItemCategory }): ReactNode {
75
75
  }
76
76
 
77
77
  function CardLink({ item }: { item: PropSidebarItemLink }): ReactNode {
78
- // We update Docusaurus `link` icon to EUI `popout` icon
79
- const icon = isInternalUrl(item.href) ? 'document' : 'popout';
78
+ // We update Docusaurus `link` icon to EUI `external` icon
79
+ const icon = isInternalUrl(item.href) ? 'document' : 'external';
80
80
  const doc = useDocById(item.docId ?? undefined);
81
81
  return (
82
82
  <CardLayout
@@ -138,7 +138,7 @@ function CollapseButton({
138
138
  onClick={onClick}
139
139
  {...rest}
140
140
  >
141
- <EuiIcon size="s" type={collapsed ? 'arrowRight' : 'arrowDown'} />
141
+ <EuiIcon size="s" type={collapsed ? 'chevronSingleRight' : 'chevronSingleDown'} />
142
142
  </button>
143
143
  );
144
144
  }
@@ -40,7 +40,7 @@ const CUSTOM_LINK_COMPONENT_MAP: Record<
40
40
  },
41
41
  changelog: {
42
42
  component: NavbarItem,
43
- icon: 'cheer',
43
+ icon: 'popper',
44
44
  },
45
45
  figma: {
46
46
  component: NavbarItem,
@@ -63,9 +63,9 @@ export default function PaginatorNavLink(props: Props): JSX.Element {
63
63
  {subLabel && <div className="pagination-nav__sublabel">{subLabel}</div>}
64
64
 
65
65
  <div className="pagination-nav__label">
66
- {isPrev && <EuiIcon type="arrowLeft" />}
66
+ {isPrev && <EuiIcon type="chevronSingleLeft" />}
67
67
  {title}
68
- {isNext && <EuiIcon type="arrowRight" />}
68
+ {isNext && <EuiIcon type="chevronSingleRight" />}
69
69
  </div>
70
70
  </Link>
71
71
  );