@redocly/theme 0.4.16 → 0.5.1

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 (87) hide show
  1. package/lib/ApiOnboarding/AppCustomAttributes.d.ts +5 -0
  2. package/lib/ApiOnboarding/AppCustomAttributes.js +8 -0
  3. package/lib/ColorModeSwitcher/ColorModeSwitcher.js +23 -10
  4. package/lib/EditPageButton/EditPageButton.d.ts +1 -1
  5. package/lib/EditPageButton/EditPageButton.js +5 -1
  6. package/lib/Footer/Footer.js +2 -2
  7. package/lib/Footer/FooterColumn.js +1 -1
  8. package/lib/LastUpdated/LastUpdated.js +30 -7
  9. package/lib/Markdown/Admonition.js +3 -0
  10. package/lib/Markdown/CodeSample/CodeSample.js +14 -14
  11. package/lib/Markdown/MarkdownLayout.d.ts +1 -1
  12. package/lib/Markdown/MarkdownLayout.js +5 -1
  13. package/lib/Navbar/Navbar.js +2 -2
  14. package/lib/Navbar/NavbarItem.js +1 -1
  15. package/lib/PageNavigation/NextPageLink.js +30 -7
  16. package/lib/PageNavigation/PageNavigation.js +8 -3
  17. package/lib/PageNavigation/PreviousPageLink.js +4 -4
  18. package/lib/Sidebar/MenuLink.d.ts +3 -4
  19. package/lib/Sidebar/MenuLink.js +2 -6
  20. package/lib/Sidebar/SidebarLayout.js +2 -2
  21. package/lib/Sidebar/types/MenuStyle.js +0 -1
  22. package/lib/TableOfContent/TableOfContent.d.ts +0 -1
  23. package/lib/TableOfContent/TableOfContent.js +5 -7
  24. package/lib/config.d.ts +385 -0
  25. package/lib/config.js +113 -0
  26. package/lib/globalStyle.js +3 -5
  27. package/lib/hooks/index.d.ts +1 -0
  28. package/lib/hooks/index.js +1 -0
  29. package/lib/hooks/useActiveHeading.js +1 -1
  30. package/lib/hooks/useActiveSectionId.js +1 -1
  31. package/lib/hooks/useThemeConfig.d.ts +1 -0
  32. package/lib/hooks/useThemeConfig.js +6 -0
  33. package/lib/icons/ColorModeIcon/ColorModeIcon.js +3 -3
  34. package/lib/index.d.ts +2 -0
  35. package/lib/index.js +2 -0
  36. package/lib/mocks/Link.js +12 -2
  37. package/lib/mocks/hooks/index.d.ts +2 -5
  38. package/lib/mocks/hooks/index.js +22 -6
  39. package/lib/mocks/types.d.ts +0 -11
  40. package/lib/mocks/types.js +1 -0
  41. package/lib/types/config.d.ts +5 -0
  42. package/lib/types/config.js +3 -0
  43. package/lib/ui/index.d.ts +0 -1
  44. package/lib/ui/index.js +0 -1
  45. package/lib/utils/args-typecheck.js +1 -1
  46. package/package.json +30 -34
  47. package/src/ApiOnboarding/AppCustomAttributes.tsx +6 -0
  48. package/src/ColorModeSwitcher/ColorModeSwitcher.tsx +29 -12
  49. package/src/EditPageButton/EditPageButton.tsx +6 -2
  50. package/src/Footer/Footer.tsx +2 -2
  51. package/src/Footer/FooterColumn.tsx +7 -1
  52. package/src/LastUpdated/LastUpdated.tsx +8 -6
  53. package/src/Markdown/Admonition.tsx +3 -0
  54. package/src/Markdown/CodeSample/CodeSample.tsx +15 -16
  55. package/src/Markdown/MarkdownLayout.tsx +9 -3
  56. package/src/Navbar/Navbar.tsx +2 -2
  57. package/src/Navbar/NavbarItem.tsx +1 -1
  58. package/src/PageNavigation/NextPageLink.tsx +8 -5
  59. package/src/PageNavigation/PageNavigation.tsx +7 -3
  60. package/src/PageNavigation/PreviousPageLink.tsx +7 -4
  61. package/src/Sidebar/MenuLink.tsx +3 -8
  62. package/src/Sidebar/SidebarLayout.tsx +2 -2
  63. package/src/Sidebar/types/MenuStyle.ts +0 -1
  64. package/src/TableOfContent/TableOfContent.tsx +5 -7
  65. package/src/config.ts +130 -0
  66. package/src/globalStyle.ts +3 -5
  67. package/src/hooks/index.ts +1 -0
  68. package/src/hooks/useActiveHeading.ts +3 -1
  69. package/src/hooks/useActiveSectionId.ts +1 -1
  70. package/src/hooks/useThemeConfig.ts +1 -0
  71. package/src/icons/ColorModeIcon/ColorModeIcon.tsx +3 -3
  72. package/src/index.ts +3 -0
  73. package/src/mocks/Link.tsx +8 -2
  74. package/src/mocks/hooks/index.ts +22 -9
  75. package/src/mocks/types.ts +2 -11
  76. package/{settings.yaml → src/settings.yaml} +0 -0
  77. package/src/types/config.ts +5 -0
  78. package/src/types/portal/src/shared/constants.d.ts +0 -1
  79. package/src/types/portal/src/shared/types/nav.d.ts +3 -0
  80. package/src/ui/index.tsx +0 -1
  81. package/src/utils/args-typecheck.ts +1 -1
  82. package/lib/hooks/useDefaultThemeSettings.d.ts +0 -2
  83. package/lib/hooks/useDefaultThemeSettings.js +0 -10
  84. package/lib/ui/UniversalLink.d.ts +0 -17
  85. package/lib/ui/UniversalLink.js +0 -79
  86. package/src/hooks/useDefaultThemeSettings.ts +0 -7
  87. package/src/ui/UniversalLink.tsx +0 -97
@@ -1,12 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useSidebarSiblingsData = exports.useThemeSettings = void 0;
4
- function useThemeSettings(_) {
3
+ exports.useSidebarSiblingsData = exports.useThemeConfig = void 0;
4
+ function useThemeConfig() {
5
5
  return {
6
- toc: { header: { label: 'header' }, hide: false },
6
+ search: { hide: false, placement: 'navbar' },
7
+ markdown: {
8
+ toc: { maxDepth: 3, header: 'Table of contents', hide: false },
9
+ lastUpdatedBlock: { hide: false, format: 'timeago', locale: 'en-US' },
10
+ copyCodeSnippet: {
11
+ hide: false,
12
+ buttonText: 'Copy',
13
+ tooltipText: 'Copy to clipboard',
14
+ toasterText: 'Copied',
15
+ toasterDuration: 1500,
16
+ },
17
+ editPage: {
18
+ baseUrl: '',
19
+ text: 'Edit this page',
20
+ },
21
+ frontmatterKeysToResolve: ['image', 'links'],
22
+ },
7
23
  navigation: {
8
- nextPageLink: { label: 'next page theme settings label' },
9
- prevPageLink: { label: 'prev page theme settings label' },
24
+ nextPageLink: { label: 'next page theme config label' },
25
+ prevPageLink: { label: 'prev page theme config label' },
10
26
  },
11
27
  colorMode: {
12
28
  modes: ['light', 'dark'],
@@ -14,7 +30,7 @@ function useThemeSettings(_) {
14
30
  },
15
31
  };
16
32
  }
17
- exports.useThemeSettings = useThemeSettings;
33
+ exports.useThemeConfig = useThemeConfig;
18
34
  function useSidebarSiblingsData() {
19
35
  return {
20
36
  nextPage: {
@@ -1,14 +1,3 @@
1
1
  export declare type ActiveItem<T> = T;
2
2
  export declare type SearchDocument = any;
3
3
  export declare type OperationParameter = any;
4
- export interface RawTheme {
5
- name: string;
6
- settings: {
7
- lastUpdatedBlock?: {
8
- hide?: boolean;
9
- format?: 'timeago' | 'iso' | 'short' | 'long';
10
- locale?: string;
11
- };
12
- [k: string]: any;
13
- };
14
- }
@@ -1,3 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ // TODO: get rid of these types
3
4
  //# sourceMappingURL=types.js.map
@@ -0,0 +1,5 @@
1
+ export declare type DefaultThemeUIConfig = {
2
+ lastUpdatedBlock?: {
3
+ hide?: boolean;
4
+ };
5
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=config.js.map
package/lib/ui/index.d.ts CHANGED
@@ -5,6 +5,5 @@ export * from '../ui/Box';
5
5
  export * from '../ui/Dropdown';
6
6
  export * from '../ui/Flex';
7
7
  export * from '../ui/Jumbotron';
8
- export * from '../ui/UniversalLink';
9
8
  export declare const LandingLayout: ({ children }: React.PropsWithChildren<object>) => React.ReactNode;
10
9
  export declare const EmptyLayout: ({ children }: React.PropsWithChildren<object>) => React.ReactNode;
package/lib/ui/index.js CHANGED
@@ -21,7 +21,6 @@ __exportStar(require("../ui/Box"), exports);
21
21
  __exportStar(require("../ui/Dropdown"), exports);
22
22
  __exportStar(require("../ui/Flex"), exports);
23
23
  __exportStar(require("../ui/Jumbotron"), exports);
24
- __exportStar(require("../ui/UniversalLink"), exports);
25
24
  const LandingLayout = ({ children }) => children;
26
25
  exports.LandingLayout = LandingLayout;
27
26
  exports.EmptyLayout = exports.LandingLayout;
@@ -8,7 +8,7 @@ function isEmptyArray(items) {
8
8
  exports.isEmptyArray = isEmptyArray;
9
9
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
10
10
  function isPrimitive(arg) {
11
- return ['string', 'boolean', 'number'].includes(typeof arg);
11
+ return ['string', 'boolean', 'number', 'undefined'].includes(typeof arg);
12
12
  }
13
13
  exports.isPrimitive = isPrimitive;
14
14
  //# sourceMappingURL=args-typecheck.js.map
package/package.json CHANGED
@@ -1,38 +1,37 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.4.16",
3
+ "version": "0.5.1",
4
4
  "description": "Shared UI components",
5
5
  "author": "team@redocly.com",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "main": "lib/index.js",
8
8
  "types": "lib/index.d.ts",
9
9
  "exports": {
10
+ "./package.json": "./package.json",
11
+ "./config.js": "./lib/config.js",
12
+ "./config": "./lib/config.js",
10
13
  ".": "./lib/index.js",
11
14
  "./Sidebar/*": "./lib/Sidebar/*.js",
12
15
  "./*": "./lib/*/index.js",
13
16
  "./src/": "./src/"
14
17
  },
15
18
  "scripts": {
16
- "start": "npm-run-all --parallel storybook generate-tokens:watch",
17
- "storybook": "start-storybook -p 6006",
18
- "build-storybook": "npm run generate-tokens && build-storybook",
19
- "generate-tokens": "ts-node scripts/generate-css-tokens.ts",
20
- "generate-tokens:watch": "ts-node-dev --respawn scripts/generate-css-tokens.ts",
19
+ "start": "npm-run-all --parallel storybook storybook:tokens:watch",
20
+ "watch": "tsc -p tsconfig.build.json && (concurrently \"tsc -w -p tsconfig.build.json\" \"tsc-alias -w -p tsconfig.build.json\")",
21
+ "ts:check": "tsc --noEmit --skipLibCheck",
21
22
  "clean": "rm -rf lib",
22
23
  "compile": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
23
24
  "build": "npm run clean && npm run compile",
24
- "build:on-rebuild": "tsc-alias -p tsconfig.build.json",
25
- "build:watch": "npm run clean && tsc-watch --project tsconfig.build.json --onSuccess \"npm run build:on-rebuild\"",
26
- "lint": "eslint . --ext .ts,.tsx --cache",
27
- "lint:fix": "eslint . --ext .ts,.tsx --fix",
28
- "prettier-base": "prettier \"**/*.{json,js,jsx,ts,tsx,yml,yaml,html,md}\"",
29
- "prettier": "npm run prettier-base -- --write",
30
- "prettier:check": "npm run prettier-base -- --check",
31
- "ts:check": "tsc --noEmit --skipLibCheck",
32
25
  "test": "jest",
26
+ "test:update": "jest -u",
33
27
  "test:watch": "jest --watch",
34
28
  "test:coverage": "jest --coverage",
35
- "test:updateSnapshot": "jest --updateSnapshot",
29
+ "test:coverage:html": "jest --coverage --coverageReporters html",
30
+ "e2e": "npx chromatic -b storybook:build --auto-accept-changes main --exit-once-uploaded",
31
+ "storybook": "start-storybook -p 6006",
32
+ "storybook:build": "npm run storybook:tokens && build-storybook",
33
+ "storybook:tokens": "ts-node scripts/generate-css-tokens.ts",
34
+ "storybook:tokens:watch": "ts-node-dev --respawn scripts/generate-css-tokens.ts",
36
35
  "chromatic": "chromatic --exit-zero-on-changes"
37
36
  },
38
37
  "peerDependencies": {
@@ -41,8 +40,9 @@
41
40
  "react": "^17.0.2",
42
41
  "react-dom": "^17.0.2",
43
42
  "react-router-dom": "^5.3.0",
44
- "styled-components": "^5.3.1",
45
- "styled-system": "^5.1.5"
43
+ "styled-components": "^5.3.6",
44
+ "styled-system": "^5.1.5",
45
+ "zod": ">=3.19.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@storybook/addon-actions": "^6.5.9",
@@ -55,7 +55,6 @@
55
55
  "@storybook/core-common": "^6.5.9",
56
56
  "@storybook/manager-webpack5": "^6.5.9",
57
57
  "@storybook/node-logger": "^6.5.9",
58
- "@storybook/preset-create-react-app": "^4.1.0",
59
58
  "@storybook/react": "^6.5.9",
60
59
  "@storybook/testing-library": "^0.0.11",
61
60
  "@storybook/theming": "^6.5.9",
@@ -75,34 +74,31 @@
75
74
  "@types/styled-system": "^5.1.13",
76
75
  "@typescript-eslint/eslint-plugin": "^5.23.0",
77
76
  "@typescript-eslint/parser": "^5.23.0",
78
- "chromatic": "^6.5.4",
79
- "esbuild": "^0.14.38",
80
- "jest": "^29.1.2",
81
- "jest-environment-jsdom": "^29.1.2",
77
+ "chromatic": "^6.10.2",
78
+ "esbuild": "^0.15.11",
79
+ "jest": "^29.2.0",
80
+ "jest-environment-jsdom": "^29.2.0",
82
81
  "jest-styled-components": "^7.1.1",
83
82
  "jest-when": "^3.5.1",
84
- "lint-staged": "^12.4.1",
85
- "lodash": "^4.17.21",
86
83
  "lodash.throttle": "^4.1.1",
87
84
  "npm-run-all": "^4.1.5",
88
- "prettier": "2.6.2",
89
85
  "react": "^17.0.2",
90
86
  "react-dom": "^17.0.2",
91
- "react-refresh": "^0.13.0",
87
+ "react-refresh": "^0.14.0",
92
88
  "react-router-dom": "^5.3.0",
93
- "react-scripts": "5.0.1",
94
89
  "storybook-addon-pseudo-states": "^1.15.1",
95
- "storybook-design-token": "^2.1.0",
96
- "styled-components": "^5.3.5",
90
+ "storybook-design-token": "^2.7.1",
91
+ "styled-components": "^5.3.6",
97
92
  "styled-system": "^5.1.5",
98
93
  "ts-jest": "^29.0.3",
99
94
  "ts-node": "^10.7.0",
100
- "ts-node-dev": "^1.1.8",
101
- "tsc-alias": "^1.6.7",
102
- "tsc-watch": "^5.0.3",
95
+ "ts-node-dev": "^2.0.0",
96
+ "tsc-alias": "^1.7.0",
103
97
  "tsconfig-paths-webpack-plugin": "^3.5.2",
104
- "typescript": "^4.6.4",
105
- "webpack": "^5.72.0"
98
+ "typescript": "^4.8.4",
99
+ "webpack": "^5.72.0",
100
+ "concurrently": "^7.4.0",
101
+ "zod": ">=3.19.1"
106
102
  },
107
103
  "dependencies": {
108
104
  "timeago.js": "^4.0.2"
@@ -0,0 +1,6 @@
1
+ interface AppCustomAttributesProps {
2
+ onChange: (value: Record<string, string>) => void;
3
+ }
4
+ export function AppCustomAttributes(_props: AppCustomAttributesProps) {
5
+ return null;
6
+ }
@@ -2,28 +2,26 @@ import React, { useState } from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
4
  import { ColorModeIcon } from '@theme/icons/ColorModeIcon';
5
- import { useMount } from '@theme/hooks';
6
- import { useDefaultThemeSettings } from '@theme/hooks/useDefaultThemeSettings';
5
+ import { useMount, useThemeConfig } from '@theme/hooks';
7
6
 
8
7
  export function ColorModeSwitcher(): JSX.Element | null {
9
- const themeSettings = useDefaultThemeSettings();
8
+ const themeSettings = useThemeConfig();
10
9
  const colorMode = themeSettings.colorMode;
11
10
  const [activeColorMode, setActiveColorMode] = useState('');
11
+ const modes = colorMode?.modes || ['light', 'dark'];
12
+ const defaultColor = colorMode?.default || modes[0] || 'light';
12
13
 
13
14
  useMount(() => {
14
- setActiveColorMode(document.documentElement.className || colorMode?.default);
15
+ setActiveColorMode(document.documentElement.className || defaultColor);
15
16
  });
16
17
 
17
- if (!colorMode?.modes || colorMode?.hide) {
18
+ if (colorMode?.hide) {
18
19
  return null;
19
20
  }
20
21
 
21
22
  const handelChangeColorMode = () => {
22
- const activeIndex = colorMode.modes.indexOf(activeColorMode);
23
- const mode =
24
- activeIndex < colorMode.modes.length - 1
25
- ? colorMode.modes[activeIndex + 1]
26
- : colorMode.modes[0];
23
+ const activeIndex = modes.indexOf(activeColorMode);
24
+ const mode = activeIndex < modes.length - 1 ? modes[activeIndex + 1] : modes[0];
27
25
  setActiveColorMode(mode);
28
26
  localStorage.setItem('colorSchema', mode);
29
27
  document.documentElement.className = `${mode} notransition`;
@@ -37,16 +35,35 @@ export function ColorModeSwitcher(): JSX.Element | null {
37
35
  <Wrapper
38
36
  data-component-name="ColorModeSwitcher/ColorModeSwitcher"
39
37
  onClick={handelChangeColorMode}
38
+ modes={modes}
40
39
  >
41
- <ColorModeIcon mode={activeColorMode} />
40
+ {modes.map((mode) => (
41
+ <ColorModeIcon mode={mode} key={mode} />
42
+ ))}
42
43
  </Wrapper>
43
44
  );
44
45
  }
45
46
 
46
- const Wrapper = styled.div`
47
+ const Wrapper = styled.div<{ modes: string[] }>`
47
48
  margin-left: var(--navbar-item-padding-horizontal);
48
49
  display: flex;
49
50
  align-items: center;
50
51
  cursor: pointer;
51
52
  user-select: none;
53
+
54
+ ${ColorModeIcon} {
55
+ display: none;
56
+ }
57
+
58
+ ${({ modes }: { modes: string[] }) => {
59
+ const items = modes.map((mode) => {
60
+ return `
61
+ html.${mode} & ${ColorModeIcon}.${mode} {
62
+ display: block;
63
+ }
64
+ `;
65
+ });
66
+
67
+ return items.join('');
68
+ }}
52
69
  `;
@@ -6,13 +6,13 @@ import { Link } from '@portal/Link';
6
6
  export interface EditPageButtonProps {
7
7
  text: string;
8
8
  to: string;
9
- icon: string;
9
+ icon?: string;
10
10
  }
11
11
 
12
12
  export const EditPageButton = ({ text, to, icon }: EditPageButtonProps): JSX.Element => {
13
13
  return (
14
14
  <EditButton to={to}>
15
- <ButtonIcon src={icon} />
15
+ {icon ? <ButtonIcon src={icon} /> : null}
16
16
  <ButtonText>{text}</ButtonText>
17
17
  </EditButton>
18
18
  );
@@ -26,6 +26,10 @@ const EditButton = styled(Link)`
26
26
  font-size: var(--font-size-base);
27
27
  font-family: var(--font-family-base);
28
28
  text-decoration: none;
29
+
30
+ @media print {
31
+ display: none;
32
+ }
29
33
  `;
30
34
 
31
35
  const ButtonIcon = styled.img`
@@ -4,7 +4,7 @@ import styled from 'styled-components';
4
4
  import { FooterColumns } from '@theme/Footer/FooterColumns';
5
5
  import { FooterCopyright } from '@theme/Footer/FooterCopyright';
6
6
  import { isEmptyArray } from '@theme/utils';
7
- import { useDefaultThemeSettings } from '@theme/hooks/useDefaultThemeSettings';
7
+ import { useThemeConfig } from '@theme/hooks';
8
8
  import type { NavGroupRecord, ResolvedNavItem } from '@theme/types/portal';
9
9
 
10
10
  interface FooterProps {
@@ -12,7 +12,7 @@ interface FooterProps {
12
12
  }
13
13
 
14
14
  export function Footer({ data: { columns, copyrightText } }: FooterProps): JSX.Element | null {
15
- const { footer } = useDefaultThemeSettings();
15
+ const { footer } = useThemeConfig();
16
16
 
17
17
  if (isEmptyArray(columns) || !copyrightText || footer?.hide) {
18
18
  return null;
@@ -22,7 +22,13 @@ export function FooterColumn({ column }: FooterColumnProps): JSX.Element {
22
22
  {columnItem.label}
23
23
  </FooterSeparator>
24
24
  ) : (
25
- <FooterLink key={columnItemIndex} to={columnItem.link} data-cy={columnItem.label}>
25
+ <FooterLink
26
+ key={columnItemIndex}
27
+ to={columnItem.link}
28
+ external={columnItem.external}
29
+ target={columnItem.target}
30
+ data-cy={columnItem.label}
31
+ >
26
32
  {columnItem.label}
27
33
  </FooterLink>
28
34
  );
@@ -1,8 +1,8 @@
1
- import React from 'react';
1
+ import * as React from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { format } from 'timeago.js';
4
4
 
5
- import { useDefaultThemeSettings } from '@theme/hooks/useDefaultThemeSettings';
5
+ import { useThemeConfig } from '@theme/hooks/useThemeConfig';
6
6
 
7
7
  const FORMATS = {
8
8
  timeago: (date: Date, locale: string) => format(date, locale),
@@ -20,18 +20,19 @@ export interface LastUpdatedProps {
20
20
  }
21
21
 
22
22
  export function LastUpdated(props: LastUpdatedProps): JSX.Element | null {
23
- const { lastUpdatedBlock } = useDefaultThemeSettings();
23
+ const { markdown: { lastUpdatedBlock = {} } = {} } = useThemeConfig();
24
24
 
25
25
  if (lastUpdatedBlock?.hide) {
26
26
  return null;
27
27
  }
28
28
 
29
29
  const lastModified = props.lastModified;
30
- const format = props.format || lastUpdatedBlock?.format || 'timeago';
31
- const locale = props.locale || lastUpdatedBlock?.locale || 'en-US';
30
+ const format = props.format || lastUpdatedBlock.format || 'timeago';
31
+ const locale = props.locale || lastUpdatedBlock.locale || 'en-US';
32
32
  const isoDate = lastModified.toISOString().split('T')[0];
33
33
 
34
- const lastUpdatedString = FORMATS[format](lastModified, locale);
34
+ const lastUpdatedString = FORMATS[format as keyof typeof FORMATS](lastModified, locale);
35
+
35
36
  const separator = format === 'timeago' ? ' ' : ' on ';
36
37
 
37
38
  return (
@@ -41,6 +42,7 @@ export function LastUpdated(props: LastUpdatedProps): JSX.Element | null {
41
42
  data-print-datetime={isoDate}
42
43
  >
43
44
  Last updated{separator}
45
+ {/* TODO: fix issue with snapshot tests - they should not depend on current date */}
44
46
  <time dateTime={isoDate}>{lastUpdatedString}</time>
45
47
  </Wrapper>
46
48
  );
@@ -36,6 +36,9 @@ const Wrapper = styled.div<AdmonitionTypeProps>`
36
36
  font-weight: var(--admonition-font-weight);
37
37
  line-height: var(--admonition-line-height);
38
38
 
39
+ print-color-adjust: exact;
40
+ -webkit-print-color-adjust: exact;
41
+
39
42
  ${({ type }) => `
40
43
  background-color: var(--admonition-${type}-background-color);
41
44
  color: var(--admonition-${type}-text-color);
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
2
2
  import styled, { css } from 'styled-components';
3
3
 
4
4
  import { ClipboardService } from '@theme/utils/ClipboardService';
5
- import { useDefaultThemeSettings } from '@theme/hooks/useDefaultThemeSettings';
5
+ import { useThemeConfig } from '@theme/hooks/useThemeConfig';
6
6
 
7
7
  export type CodeSampleProps = {
8
8
  language: string;
@@ -10,37 +10,28 @@ export type CodeSampleProps = {
10
10
  rawContent: string;
11
11
  };
12
12
 
13
- const defaultCopyCodeSnippet = {
14
- hide: false,
15
- buttonText: 'Copy',
16
- tooltipText: 'Copy the code snippet',
17
- toasterText: 'Copied',
18
- toasterDuration: 1500,
19
- };
20
-
21
13
  export function CodeSample({ rawContent, highlighted, language }: CodeSampleProps): JSX.Element {
22
14
  const langClassName = language ? `language-${language}` : '';
23
- const { copyCodeSnippet } = useDefaultThemeSettings();
24
- const copyCodeProps = { ...defaultCopyCodeSnippet, ...copyCodeSnippet };
15
+ const { markdown: { copyCodeSnippet = {} } = {} } = useThemeConfig();
25
16
 
26
17
  const [isCopied, setIsCopied] = useState(false);
27
18
 
28
19
  const copyCode = (code: string) => {
29
20
  ClipboardService.copyCustom(code);
30
21
  setIsCopied(true);
31
- setTimeout(() => setIsCopied(false), copyCodeProps.toasterDuration);
22
+ setTimeout(() => setIsCopied(false), copyCodeSnippet.toasterDuration);
32
23
  };
33
24
 
34
25
  return (
35
26
  <Wrapper className="code-sample" data-component-name="Markdown/CodeSample/CodeSample">
36
- {!copyCodeProps.hide && (
27
+ {!copyCodeSnippet.hide && (
37
28
  <CodeSampleButtonContainer onClick={() => copyCode(rawContent)}>
38
29
  {!isCopied && (
39
- <CopyCodeButton title={copyCodeProps.tooltipText}>
40
- {copyCodeProps.buttonText}
30
+ <CopyCodeButton title={copyCodeSnippet.tooltipText || 'Copy to clipboard'}>
31
+ {copyCodeSnippet.buttonText}
41
32
  </CopyCodeButton>
42
33
  )}
43
- {isCopied && <DoneIndicator>{copyCodeProps.toasterText}</DoneIndicator>}
34
+ {isCopied && <DoneIndicator>{copyCodeSnippet.toasterText}</DoneIndicator>}
44
35
  </CodeSampleButtonContainer>
45
36
  )}
46
37
  <pre className={langClassName}>
@@ -64,6 +55,10 @@ const CopyCodeButton = styled.div`
64
55
  &:hover {
65
56
  cursor: pointer;
66
57
  }
58
+
59
+ @media print {
60
+ display: none;
61
+ }
67
62
  `;
68
63
 
69
64
  const DoneIndicator = styled.div`
@@ -150,6 +145,10 @@ const Wrapper = styled.div`
150
145
  border-radius: var(--code-block-border-radius);
151
146
  font-family: var(--code-font-family);
152
147
  background-color: var(--code-block-background-color);
148
+
149
+ print-color-adjust: exact;
150
+ -webkit-print-color-adjust: exact;
151
+
153
152
  code {
154
153
  background-color: transparent;
155
154
  border: 0;
@@ -6,6 +6,7 @@ import { ContainerWrapper } from '@theme/Markdown/ContainerWrapper';
6
6
  import { PageNavigation } from '@theme/PageNavigation/PageNavigation';
7
7
  import { EditPageButton } from '@theme/EditPageButton';
8
8
  import { LastUpdated } from '@theme/LastUpdated/LastUpdated';
9
+ import { useThemeConfig } from '@theme/hooks';
9
10
 
10
11
  type MarkdownLayoutProps = {
11
12
  tableOfContent: React.ReactNode;
@@ -13,7 +14,7 @@ type MarkdownLayoutProps = {
13
14
  editPage?: {
14
15
  to: string;
15
16
  text: string;
16
- icon: string;
17
+ icon?: string;
17
18
  };
18
19
  /** String in ISO format */
19
20
  lastModified?: string | null;
@@ -25,13 +26,18 @@ export function MarkdownLayout({
25
26
  editPage,
26
27
  lastModified,
27
28
  }: MarkdownLayoutProps): JSX.Element {
29
+ const { markdown } = useThemeConfig();
30
+ const { editPage: themeEditPage } = markdown || {};
31
+
32
+ const mergedConf = editPage ? { ...editPage, ...themeEditPage } : undefined;
33
+
28
34
  return (
29
35
  <PageWrapper data-component-name="Markdown/MarkdownLayout">
30
36
  <ContainerWrapper withToc={true}>
31
37
  <LayoutTop>
32
38
  {lastModified && <LastUpdated lastModified={new Date(lastModified)} />}
33
- {editPage && (
34
- <EditPageButton text={editPage.text} to={editPage.to} icon={editPage.icon} />
39
+ {mergedConf && (
40
+ <EditPageButton text={mergedConf.text} to={mergedConf.to} icon={mergedConf.icon} />
35
41
  )}
36
42
  </LayoutTop>
37
43
  {markdownWrapper}
@@ -6,7 +6,7 @@ import { useMobileMenu } from '@theme/hooks/useMobileMenu';
6
6
  import { MobileNavbarMenuButton } from '@theme/Navbar/MobileNavbarMenuButton';
7
7
  import { MobileNavbarMenu } from '@theme/Navbar/MobileNavbarMenu';
8
8
  import { ColorModeSwitcher } from '@theme/ColorModeSwitcher/ColorModeSwitcher';
9
- import { useDefaultThemeSettings } from '@theme/hooks/useDefaultThemeSettings';
9
+ import { useThemeConfig } from '@theme/hooks/useThemeConfig';
10
10
  import type { ResolvedConfigLinks } from '@theme/types/portal';
11
11
 
12
12
  interface NavbarProps {
@@ -18,7 +18,7 @@ interface NavbarProps {
18
18
 
19
19
  export function Navbar({ menu, logo, search, profile }: NavbarProps): JSX.Element | null {
20
20
  const [isOpen, setIsOpen] = useMobileMenu(false);
21
- const { search: searchSettings, navbar } = useDefaultThemeSettings();
21
+ const { search: searchSettings, navbar } = useThemeConfig();
22
22
  const hideSearch =
23
23
  searchSettings?.hide || (searchSettings?.placement && searchSettings?.placement !== 'navbar');
24
24
 
@@ -28,7 +28,7 @@ export function NavbarItem({ navItem, className }: NavbarItemProps): JSX.Element
28
28
  data-component-name="Navbar/NavbarItem"
29
29
  className={className}
30
30
  >
31
- <NavbarLink to={item.link} active={isActive}>
31
+ <NavbarLink to={item.link} external={item.external} target={item.target} active={isActive}>
32
32
  <NavbarLabel>{item.label}</NavbarLabel>
33
33
  </NavbarLink>
34
34
  </NavbarMenuItem>
@@ -1,9 +1,9 @@
1
- import React from 'react';
1
+ import * as React from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
4
  import { useSidebarSiblingsData } from '@portal/hooks';
5
5
  import { Button } from '@theme/Button/Button';
6
- import { useDefaultThemeSettings } from '@theme/hooks/useDefaultThemeSettings';
6
+ import { useThemeConfig } from '@theme/hooks/useThemeConfig';
7
7
  import type { ResolvedNavItemWithLink } from '@theme/types/portal';
8
8
 
9
9
  interface NextPageType {
@@ -12,13 +12,16 @@ interface NextPageType {
12
12
 
13
13
  export function NextPageLink(): JSX.Element {
14
14
  const { nextPage }: NextPageType = useSidebarSiblingsData() || {};
15
- const { navigation } = useDefaultThemeSettings();
15
+ const { navigation } = useThemeConfig();
16
16
 
17
- if (!nextPage || navigation?.hide || navigation?.nextPageLink?.hide) {
17
+ if (!nextPage || navigation?.nextPageLink?.hide) {
18
18
  return <div>&nbsp;</div>;
19
19
  }
20
20
 
21
- const label = navigation?.nextPageLink?.label || `Next to ${nextPage.label}`;
21
+ const label = (navigation?.nextPageLink?.label || 'Next to {label}').replace(
22
+ '{label}',
23
+ nextPage.label || nextPage.routeSlug || '',
24
+ );
22
25
 
23
26
  return (
24
27
  <StyledButton
@@ -3,12 +3,12 @@ import styled from 'styled-components';
3
3
 
4
4
  import { PreviousPageLink } from '@theme/PageNavigation/PreviousPageLink';
5
5
  import { NextPageLink } from '@theme/PageNavigation/NextPageLink';
6
- import { useDefaultThemeSettings } from '@theme/hooks/useDefaultThemeSettings';
6
+ import { useThemeConfig } from '@theme/hooks/useThemeConfig';
7
7
 
8
8
  export function PageNavigation(): JSX.Element | null {
9
- const { navigation } = useDefaultThemeSettings();
9
+ const { navigation } = useThemeConfig();
10
10
 
11
- if (navigation?.hide) {
11
+ if (navigation?.prevPageLink?.hide && navigation?.nextPageLink?.hide) {
12
12
  return null;
13
13
  }
14
14
 
@@ -24,4 +24,8 @@ const PageNavigationWrapper = styled.div`
24
24
  display: flex;
25
25
  justify-content: space-between;
26
26
  margin: 25px 0;
27
+
28
+ @media print {
29
+ display: none;
30
+ }
27
31
  `;
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
4
  import { useSidebarSiblingsData } from '@portal/hooks';
5
- import { useDefaultThemeSettings } from '@theme/hooks/useDefaultThemeSettings';
5
+ import { useThemeConfig } from '@theme/hooks/useThemeConfig';
6
6
  import { Button } from '@theme/Button/Button';
7
7
  import type { ResolvedNavItemWithLink } from '@theme/types/portal';
8
8
 
@@ -12,13 +12,16 @@ interface PreviousPageType {
12
12
 
13
13
  export function PreviousPageLink(): JSX.Element {
14
14
  const { prevPage }: PreviousPageType = useSidebarSiblingsData() || {};
15
- const { navigation } = useDefaultThemeSettings();
15
+ const { navigation } = useThemeConfig();
16
16
 
17
- if (!prevPage || navigation?.hide || navigation?.prevPageLink?.hide) {
17
+ if (!prevPage || navigation?.prevPageLink?.hide) {
18
18
  return <div>&nbsp;</div>;
19
19
  }
20
20
 
21
- const label = navigation?.prevPageLink?.label || `Back to ${prevPage.label}`;
21
+ const label = (navigation?.prevPageLink?.label || 'Back to {label}').replace(
22
+ '{label}',
23
+ prevPage.label || prevPage.routeSlug || '',
24
+ );
22
25
 
23
26
  return (
24
27
  <StyledButton