@redocly/theme 0.53.0-next.0 → 0.53.0-next.2

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.
@@ -9,7 +9,6 @@ const styled_components_1 = __importDefault(require("styled-components"));
9
9
  const FooterItem_1 = require("../../components/Footer/FooterItem");
10
10
  const utils_1 = require("../../core/utils");
11
11
  const hooks_1 = require("../../core/hooks");
12
- const Link_1 = require("../../components/Link/Link");
13
12
  function FooterColumn({ column, className }) {
14
13
  const { useTranslate } = (0, hooks_1.useThemeHooks)();
15
14
  const { translate } = useTranslate();
@@ -17,8 +16,8 @@ function FooterColumn({ column, className }) {
17
16
  const iconsOnly = items.every((item) => item.label === item.link && (item.icon || item.srcSet));
18
17
  const label = translate(column.labelTranslationKey, column.label);
19
18
  return (react_1.default.createElement(FooterColumnWrapper, { "data-component-name": "Footer/FooterColumn", className: className },
20
- react_1.default.createElement(FooterColumnTitle, null, (0, utils_1.isNavLinkItem)(column) ? (react_1.default.createElement(Link_1.Link, { to: column.link, external: column.external, target: column.target }, label)) : (label)),
21
- react_1.default.createElement(FooterColumnItems, { iconsOnly: iconsOnly }, items === null || items === void 0 ? void 0 : items.map((columnItem, columnItemIndex) => (react_1.default.createElement(FooterItem_1.FooterItem, { item: columnItem, iconsOnly: iconsOnly, key: columnItemIndex }))))));
19
+ !(0, utils_1.isNavLinkItem)(column) && react_1.default.createElement(FooterColumnTitle, null, label),
20
+ react_1.default.createElement(FooterColumnItems, { iconsOnly: (0, utils_1.isNavLinkItem)(column) ? false : iconsOnly }, (0, utils_1.isNavLinkItem)(column) ? (react_1.default.createElement(NonGroupItem, { item: column, iconsOnly: Boolean((column === null || column === void 0 ? void 0 : column.label) === (column === null || column === void 0 ? void 0 : column.link) && ((column === null || column === void 0 ? void 0 : column.icon) || (column === null || column === void 0 ? void 0 : column.srcSet))), key: column.label, className: "nonGroupFooterItem" })) : (items === null || items === void 0 ? void 0 : items.map((columnItem, columnItemIndex) => (react_1.default.createElement(FooterItem_1.FooterItem, { item: columnItem, iconsOnly: iconsOnly, key: columnItemIndex })))))));
22
21
  }
23
22
  const FooterColumnWrapper = styled_components_1.default.div `
24
23
  display: flex;
@@ -74,4 +73,7 @@ const FooterColumnItems = styled_components_1.default.div `
74
73
  justify-content: flex-start;
75
74
  }
76
75
  `;
76
+ const NonGroupItem = (0, styled_components_1.default)(FooterItem_1.FooterItem) `
77
+ margin: 0;
78
+ `;
77
79
  //# sourceMappingURL=FooterColumn.js.map
@@ -2,5 +2,6 @@ import type { ResolvedNavItem } from '@redocly/config';
2
2
  export type FooterItemProps = {
3
3
  item: ResolvedNavItem;
4
4
  iconsOnly?: boolean;
5
+ className?: string;
5
6
  };
6
- export declare function FooterItem({ item, iconsOnly }: FooterItemProps): JSX.Element | null;
7
+ export declare function FooterItem({ item, iconsOnly, className }: FooterItemProps): JSX.Element | null;
@@ -11,7 +11,7 @@ const LaunchIcon_1 = require("../../icons/LaunchIcon/LaunchIcon");
11
11
  const Link_1 = require("../../components/Link/Link");
12
12
  const Image_1 = require("../../components/Image/Image");
13
13
  const utils_1 = require("../../core/utils");
14
- function FooterItem({ item, iconsOnly }) {
14
+ function FooterItem({ item, iconsOnly, className }) {
15
15
  const { useTranslate, useTelemetry } = (0, hooks_1.useThemeHooks)();
16
16
  const telemetry = useTelemetry();
17
17
  const { translate } = useTranslate();
@@ -20,7 +20,7 @@ function FooterItem({ item, iconsOnly }) {
20
20
  }
21
21
  const hasIcon = Boolean(item.icon || item.srcSet);
22
22
  const iconWithoutLabel = Boolean(item.label === item.link && hasIcon);
23
- return (react_1.default.createElement(FooterItemWrapper, { "data-component-name": "Footer/FooterItem", iconsOnly: iconsOnly, item: item }, item.type === 'separator' ? (react_1.default.createElement(FooterSeparator, { "data-translation-key": item.labelTranslationKey }, translate(item.labelTranslationKey, item.label))) : (react_1.default.createElement(FooterLink, { to: item.link, external: item.external, target: item.target, "data-testid": item.label, onClick: () => telemetry.send('footer_item_clicked', {}), "data-translation-key": item.labelTranslationKey },
23
+ return (react_1.default.createElement(FooterItemWrapper, { className: className, "data-component-name": "Footer/FooterItem", iconsOnly: iconsOnly, item: item }, item.type === 'separator' ? (react_1.default.createElement(FooterSeparator, { "data-translation-key": item.labelTranslationKey }, translate(item.labelTranslationKey, item.label))) : (react_1.default.createElement(FooterLink, { to: item.link, external: item.external, target: item.target, "data-testid": item.label, onClick: () => telemetry.send('footer_item_clicked', {}), "data-translation-key": item.labelTranslationKey },
24
24
  hasIcon ? (react_1.default.createElement(FooterLinkIcon, { iconsOnly: iconsOnly },
25
25
  react_1.default.createElement(Image_1.Image, { src: item.icon, srcSet: item.srcSet }))) : null,
26
26
  !iconWithoutLabel ? translate(item.labelTranslationKey, item.label) : null,
@@ -58,7 +58,7 @@ function useNestedMenu({ item, labelRef, nestedMenuRef }) {
58
58
  }, [labelRef, isExpanded, item.active]);
59
59
  const handleExpand = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () {
60
60
  if (item.expanded === 'always' ||
61
- (item.link && item.hasActiveSubItem && item.link !== location.pathname)) {
61
+ (item.link && item.hasActiveSubItem && item.link !== (0, utils_1.withoutPathPrefix)(location.pathname))) {
62
62
  return;
63
63
  }
64
64
  const [firstChild] = item.items;
@@ -36,7 +36,7 @@ const buildLanguagesGroup = (locales, defaultLocale, currentLocale) => {
36
36
  items: locales
37
37
  .filter((locale) => locale.code !== currentLocale)
38
38
  .map((locale) => {
39
- const newLangPathname = (0, utils_1.withPathPrefix)((0, utils_1.getPathnameForLocale)((0, utils_1.withoutPathPrefix)(location.pathname), defaultLocale, locale.code, locales));
39
+ const newLangPathname = (0, utils_1.getPathnameForLocale)((0, utils_1.withoutPathPrefix)(location.pathname), defaultLocale, locale.code, locales);
40
40
  const newUrlWithLanguage = `${newLangPathname}${location.search}${location.hash}`;
41
41
  return {
42
42
  type: 'link',
@@ -25,10 +25,17 @@ export declare function slash(path: string): string;
25
25
  export declare function addTrailingSlash(url: string): string;
26
26
  /**
27
27
  * Adds locale to pathname, or replaces current locale in pathname with a new one
28
- * @param originalPathname - Pathname without api prefix
28
+ * @param originalPathname - Pathname without path prefix
29
29
  * @param defaultLocale - Default locale code
30
30
  * @param newLocale - New locale code to apply
31
31
  * @param allLocales - Array of all available locales
32
32
  */
33
33
  export declare function getPathnameForLocale(originalPathname: string, defaultLocale: string, newLocale: string, allLocales: Locale[]): string;
34
+ /**
35
+ * Extracts the locale code from a pathname
36
+ * @param pathname - URL pathname to extract locale from without path prefix
37
+ * @param defaultLocale - Default locale code to return if no locale found in pathname
38
+ * @param allLocales - Array of all available locales to check against
39
+ * @returns The locale code from the pathname, or the default locale if none found
40
+ */
34
41
  export declare function getLocaleFromPathname(pathname: string, defaultLocale?: string, allLocales?: Locale[]): string;
@@ -84,7 +84,7 @@ function addTrailingSlash(url) {
84
84
  }
85
85
  /**
86
86
  * Adds locale to pathname, or replaces current locale in pathname with a new one
87
- * @param originalPathname - Pathname without api prefix
87
+ * @param originalPathname - Pathname without path prefix
88
88
  * @param defaultLocale - Default locale code
89
89
  * @param newLocale - New locale code to apply
90
90
  * @param allLocales - Array of all available locales
@@ -100,9 +100,15 @@ function getPathnameForLocale(originalPathname, defaultLocale, newLocale, allLoc
100
100
  const newLocalePrefix = newLocale === defaultLocale ? '' : '/' + newLocale;
101
101
  return `${newLocalePrefix.toLowerCase()}${pathnameWithoutLocale}`;
102
102
  }
103
+ /**
104
+ * Extracts the locale code from a pathname
105
+ * @param pathname - URL pathname to extract locale from without path prefix
106
+ * @param defaultLocale - Default locale code to return if no locale found in pathname
107
+ * @param allLocales - Array of all available locales to check against
108
+ * @returns The locale code from the pathname, or the default locale if none found
109
+ */
103
110
  function getLocaleFromPathname(pathname, defaultLocale = constants_1.DEFAULT_LOCALE_PLACEHOLDER, allLocales = []) {
104
- var _a;
105
- const maybeLocale = (_a = withoutPathPrefix(pathname)) === null || _a === void 0 ? void 0 : _a.split('/')[1];
111
+ const maybeLocale = pathname === null || pathname === void 0 ? void 0 : pathname.split('/')[1];
106
112
  const locale = allLocales.find((locale) => locale.code.toLowerCase() === maybeLocale);
107
113
  return (locale === null || locale === void 0 ? void 0 : locale.code) || defaultLocale;
108
114
  }
@@ -14,15 +14,30 @@ function testIconComponent(IconComponent, componentName) {
14
14
  const { container } = (0, react_2.render)(react_1.default.createElement(IconComponent, null));
15
15
  const svgElement = container.querySelector('svg');
16
16
  expect(svgElement).toBeInTheDocument();
17
- expect(container.firstChild).toHaveStyle('height: 16px; width: 16px;');
17
+ // Check if styles are applied either via style attribute or CSS class
18
+ const element = container.firstChild;
19
+ const hasStyleAttr = element.style.height === '16px' && element.style.width === '16px';
20
+ const elementClass = element.getAttribute('class') || '';
21
+ const hasClassName = elementClass.includes('sc-'); // styled-components class
22
+ expect(hasStyleAttr || hasClassName).toBeTruthy();
18
23
  },
19
24
  appliesCustomSizeAndColor: () => {
25
+ var _a;
20
26
  const { container } = (0, react_2.render)(react_1.default.createElement(IconComponent, { size: "24px", color: "--color-primary" }));
21
27
  const svgElement = container.querySelector('svg');
22
28
  expect(svgElement).toBeInTheDocument();
23
- expect(container.firstChild).toHaveStyle('height: 24px; width: 24px;');
29
+ const element = container.firstChild;
24
30
  const pathElement = container.querySelector('path');
25
- expect(pathElement).toHaveStyle('fill: var(--color-primary);');
31
+ // Check if size styles are applied either via style attribute or CSS class
32
+ const hasSizeStyle = element.style.height === '24px' && element.style.width === '24px';
33
+ const elementClass = element.getAttribute('class') || '';
34
+ const hasClassName = elementClass.includes('sc-'); // styled-components class
35
+ expect(hasSizeStyle || hasClassName).toBeTruthy();
36
+ // Check if color is applied either via style attribute or CSS class
37
+ const hasColorStyle = ((_a = pathElement.getAttribute('style')) === null || _a === void 0 ? void 0 : _a.includes('fill: var(--color-primary)')) ||
38
+ pathElement.getAttribute('fill') === 'var(--color-primary)' ||
39
+ elementClass.includes('sc-'); // check styled-components class on the root element
40
+ expect(hasColorStyle).toBeTruthy();
26
41
  },
27
42
  hasCorrectDataComponentName: () => {
28
43
  const { container } = (0, react_2.render)(react_1.default.createElement(IconComponent, null));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.53.0-next.0",
3
+ "version": "0.53.0-next.2",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -88,7 +88,7 @@
88
88
  "react-calendar": "4.2.1",
89
89
  "react-date-picker": "10.0.3",
90
90
  "timeago.js": "4.0.2",
91
- "@redocly/config": "0.23.0"
91
+ "@redocly/config": "0.24.0"
92
92
  },
93
93
  "scripts": {
94
94
  "watch": "tsc -p tsconfig.build.json && (concurrently \"tsc -w -p tsconfig.build.json\" \"tsc-alias -w -p tsconfig.build.json\")",
@@ -6,7 +6,6 @@ import type { ResolvedNavItem } from '@redocly/config';
6
6
  import { FooterItem } from '@redocly/theme/components/Footer/FooterItem';
7
7
  import { breakpoints, isNavLinkItem } from '@redocly/theme/core/utils';
8
8
  import { useThemeHooks } from '@redocly/theme/core/hooks';
9
- import { Link } from '@redocly/theme/components/Link/Link';
10
9
 
11
10
  export type FooterColumnProps = {
12
11
  column: ResolvedNavItem;
@@ -20,22 +19,22 @@ export function FooterColumn({ column, className }: FooterColumnProps): JSX.Elem
20
19
 
21
20
  const iconsOnly = items.every((item) => item.label === item.link && (item.icon || item.srcSet));
22
21
  const label = translate(column.labelTranslationKey, column.label);
23
-
24
22
  return (
25
23
  <FooterColumnWrapper data-component-name="Footer/FooterColumn" className={className}>
26
- <FooterColumnTitle>
24
+ {!isNavLinkItem(column) && <FooterColumnTitle>{label}</FooterColumnTitle>}
25
+ <FooterColumnItems iconsOnly={isNavLinkItem(column) ? false : iconsOnly}>
27
26
  {isNavLinkItem(column) ? (
28
- <Link to={column.link} external={column.external} target={column.target}>
29
- {label}
30
- </Link>
27
+ <NonGroupItem
28
+ item={column}
29
+ iconsOnly={Boolean(column?.label === column?.link && (column?.icon || column?.srcSet))}
30
+ key={column.label}
31
+ className="nonGroupFooterItem"
32
+ />
31
33
  ) : (
32
- label
34
+ items?.map((columnItem, columnItemIndex) => (
35
+ <FooterItem item={columnItem} iconsOnly={iconsOnly} key={columnItemIndex} />
36
+ ))
33
37
  )}
34
- </FooterColumnTitle>
35
- <FooterColumnItems iconsOnly={iconsOnly}>
36
- {items?.map((columnItem, columnItemIndex) => (
37
- <FooterItem item={columnItem} iconsOnly={iconsOnly} key={columnItemIndex} />
38
- ))}
39
38
  </FooterColumnItems>
40
39
  </FooterColumnWrapper>
41
40
  );
@@ -97,3 +96,7 @@ const FooterColumnItems = styled.div<{ iconsOnly: boolean }>`
97
96
  justify-content: flex-start;
98
97
  }
99
98
  `;
99
+
100
+ const NonGroupItem = styled(FooterItem)`
101
+ margin: 0;
102
+ `;
@@ -12,9 +12,10 @@ import { breakpoints } from '@redocly/theme/core/utils';
12
12
  export type FooterItemProps = {
13
13
  item: ResolvedNavItem;
14
14
  iconsOnly?: boolean;
15
+ className?: string;
15
16
  };
16
17
 
17
- export function FooterItem({ item, iconsOnly }: FooterItemProps): JSX.Element | null {
18
+ export function FooterItem({ item, iconsOnly, className }: FooterItemProps): JSX.Element | null {
18
19
  const { useTranslate, useTelemetry } = useThemeHooks();
19
20
  const telemetry = useTelemetry();
20
21
  const { translate } = useTranslate();
@@ -24,9 +25,13 @@ export function FooterItem({ item, iconsOnly }: FooterItemProps): JSX.Element |
24
25
  }
25
26
  const hasIcon = Boolean(item.icon || item.srcSet);
26
27
  const iconWithoutLabel = Boolean(item.label === item.link && hasIcon);
27
-
28
28
  return (
29
- <FooterItemWrapper data-component-name="Footer/FooterItem" iconsOnly={iconsOnly} item={item}>
29
+ <FooterItemWrapper
30
+ className={className}
31
+ data-component-name="Footer/FooterItem"
32
+ iconsOnly={iconsOnly}
33
+ item={item}
34
+ >
30
35
  {item.type === 'separator' ? (
31
36
  <FooterSeparator data-translation-key={item.labelTranslationKey}>
32
37
  {translate(item.labelTranslationKey, item.label)}
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useState } from 'react';
4
4
  import type { MenuItemProps } from '@redocly/theme/core/types';
5
5
 
6
6
  import { useMenuItemExpanded, useCollapse } from '@redocly/theme/core/hooks';
7
- import { loadAndNavigate } from '@redocly/theme/core/utils';
7
+ import { loadAndNavigate, withoutPathPrefix } from '@redocly/theme/core/utils';
8
8
 
9
9
  type NestedMenuProps = MenuItemProps & {
10
10
  labelRef?: React.RefObject<HTMLElement>;
@@ -61,7 +61,7 @@ export function useNestedMenu({ item, labelRef, nestedMenuRef }: NestedMenuProps
61
61
  const handleExpand = useCallback(async () => {
62
62
  if (
63
63
  item.expanded === 'always' ||
64
- (item.link && item.hasActiveSubItem && item.link !== location.pathname)
64
+ (item.link && item.hasActiveSubItem && item.link !== withoutPathPrefix(location.pathname))
65
65
  ) {
66
66
  return;
67
67
  }
@@ -64,13 +64,11 @@ export const buildLanguagesGroup = (
64
64
  items: locales
65
65
  .filter((locale) => locale.code !== currentLocale)
66
66
  .map((locale) => {
67
- const newLangPathname = withPathPrefix(
68
- getPathnameForLocale(
69
- withoutPathPrefix(location.pathname),
70
- defaultLocale,
71
- locale.code,
72
- locales,
73
- ),
67
+ const newLangPathname = getPathnameForLocale(
68
+ withoutPathPrefix(location.pathname),
69
+ defaultLocale,
70
+ locale.code,
71
+ locales,
74
72
  );
75
73
  const newUrlWithLanguage = `${newLangPathname}${location.search}${location.hash}`;
76
74
  return {
@@ -88,7 +88,7 @@ export function addTrailingSlash(url: string): string {
88
88
 
89
89
  /**
90
90
  * Adds locale to pathname, or replaces current locale in pathname with a new one
91
- * @param originalPathname - Pathname without api prefix
91
+ * @param originalPathname - Pathname without path prefix
92
92
  * @param defaultLocale - Default locale code
93
93
  * @param newLocale - New locale code to apply
94
94
  * @param allLocales - Array of all available locales
@@ -115,12 +115,19 @@ export function getPathnameForLocale(
115
115
  return `${newLocalePrefix.toLowerCase()}${pathnameWithoutLocale}`;
116
116
  }
117
117
 
118
+ /**
119
+ * Extracts the locale code from a pathname
120
+ * @param pathname - URL pathname to extract locale from without path prefix
121
+ * @param defaultLocale - Default locale code to return if no locale found in pathname
122
+ * @param allLocales - Array of all available locales to check against
123
+ * @returns The locale code from the pathname, or the default locale if none found
124
+ */
118
125
  export function getLocaleFromPathname(
119
126
  pathname: string,
120
127
  defaultLocale: string = DEFAULT_LOCALE_PLACEHOLDER,
121
128
  allLocales: Locale[] = [],
122
129
  ) {
123
- const maybeLocale = withoutPathPrefix(pathname)?.split('/')[1];
130
+ const maybeLocale = pathname?.split('/')[1];
124
131
  const locale = allLocales.find((locale) => locale.code.toLowerCase() === maybeLocale);
125
132
 
126
133
  return locale?.code || defaultLocale;
@@ -10,7 +10,14 @@ export function testIconComponent(IconComponent: React.ComponentType<any>, compo
10
10
  const { container } = render(<IconComponent />);
11
11
  const svgElement = container.querySelector('svg');
12
12
  expect(svgElement).toBeInTheDocument();
13
- expect(container.firstChild).toHaveStyle('height: 16px; width: 16px;');
13
+
14
+ // Check if styles are applied either via style attribute or CSS class
15
+ const element = container.firstChild as HTMLElement | SVGElement;
16
+ const hasStyleAttr = element.style.height === '16px' && element.style.width === '16px';
17
+ const elementClass = element.getAttribute('class') || '';
18
+ const hasClassName = elementClass.includes('sc-'); // styled-components class
19
+
20
+ expect(hasStyleAttr || hasClassName).toBeTruthy();
14
21
  },
15
22
 
16
23
  appliesCustomSizeAndColor: () => {
@@ -18,9 +25,24 @@ export function testIconComponent(IconComponent: React.ComponentType<any>, compo
18
25
 
19
26
  const svgElement = container.querySelector('svg');
20
27
  expect(svgElement).toBeInTheDocument();
21
- expect(container.firstChild).toHaveStyle('height: 24px; width: 24px;');
22
- const pathElement = container.querySelector('path');
23
- expect(pathElement).toHaveStyle('fill: var(--color-primary);');
28
+
29
+ const element = container.firstChild as HTMLElement | SVGElement;
30
+ const pathElement = container.querySelector('path') as SVGPathElement;
31
+
32
+ // Check if size styles are applied either via style attribute or CSS class
33
+ const hasSizeStyle = element.style.height === '24px' && element.style.width === '24px';
34
+ const elementClass = element.getAttribute('class') || '';
35
+ const hasClassName = elementClass.includes('sc-'); // styled-components class
36
+
37
+ expect(hasSizeStyle || hasClassName).toBeTruthy();
38
+
39
+ // Check if color is applied either via style attribute or CSS class
40
+ const hasColorStyle =
41
+ pathElement.getAttribute('style')?.includes('fill: var(--color-primary)') ||
42
+ pathElement.getAttribute('fill') === 'var(--color-primary)' ||
43
+ elementClass.includes('sc-'); // check styled-components class on the root element
44
+
45
+ expect(hasColorStyle).toBeTruthy();
24
46
  },
25
47
 
26
48
  hasCorrectDataComponentName: () => {