@db-ux/react-core-components 4.5.2 → 4.5.4
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.
- package/CHANGELOG.md +14 -0
- package/dist/components/navigation-item/model.d.ts +2 -0
- package/dist/components/navigation-item/navigation-item.js +10 -2
- package/dist/shared/showcase/show-code-link.d.ts +7 -0
- package/dist/shared/showcase/show-code-link.js +51 -0
- package/dist/utils/show-code-link.spec.d.ts +1 -0
- package/dist/utils/show-code-link.spec.js +28 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @db-ux/react-core-components
|
|
2
2
|
|
|
3
|
+
## 4.5.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- refactor: Enhance the accessibility of the navigation components by adding missing WAI-ARIA attributes (`aria-controls`, `aria-label`, `aria-haspopup`) for better screen reader support. - [see commit f040188](https://github.com/db-ux-design-system/core-web/commit/f0401886267359518c37062373bb0f9df311214c)
|
|
8
|
+
|
|
9
|
+
- fix: adjusted `@media` queries (removed `screen` from combined queries) to avoid broken layouts when printing - [see commit 0960eb6](https://github.com/db-ux-design-system/core-web/commit/0960eb6f9223fe23bdaac0de685cf7aa5eb7654e)
|
|
10
|
+
|
|
11
|
+
- fix(switch): removed obsolete gap towards label in `small` variant - [see commit 40bad01](https://github.com/db-ux-design-system/core-web/commit/40bad017f068bf8968829977808a6d8e9d21b4fe)
|
|
12
|
+
|
|
13
|
+
## 4.5.3
|
|
14
|
+
|
|
15
|
+
_version bump_
|
|
16
|
+
|
|
3
17
|
## 4.5.2
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -29,5 +29,7 @@ export type DBNavigationItemDefaultState = {
|
|
|
29
29
|
hasSubNavigation?: boolean;
|
|
30
30
|
navigationItemSafeTriangle?: NavigationItemSafeTriangle;
|
|
31
31
|
autoClose?: boolean;
|
|
32
|
+
subNavigationId?: string;
|
|
33
|
+
subNavigationToggleId?: string;
|
|
32
34
|
};
|
|
33
35
|
export type DBNavigationItemState = DBNavigationItemDefaultState & ClickEventState<HTMLButtonElement> & GlobalState & InitializedState & NavigationBehaviorState;
|
|
@@ -6,8 +6,10 @@ import { DEFAULT_BACK } from "../../shared/constants";
|
|
|
6
6
|
import { cls, delay, getBoolean, getBooleanAsString } from "../../utils";
|
|
7
7
|
import { NavigationItemSafeTriangle } from "../../utils/navigation";
|
|
8
8
|
import DBButton from "../button/button";
|
|
9
|
+
import { useId } from "react";
|
|
9
10
|
function DBNavigationItemFn(props, component) {
|
|
10
11
|
var _a, _b, _c;
|
|
12
|
+
const uuid = useId();
|
|
11
13
|
const _ref = component || useRef(component);
|
|
12
14
|
const [initialized, setInitialized] = useState(() => false);
|
|
13
15
|
const [hasAreaPopup, setHasAreaPopup] = useState(() => false);
|
|
@@ -15,6 +17,8 @@ function DBNavigationItemFn(props, component) {
|
|
|
15
17
|
const [isSubNavigationExpanded, setIsSubNavigationExpanded] = useState(() => false);
|
|
16
18
|
const [autoClose, setAutoClose] = useState(() => false);
|
|
17
19
|
const [navigationItemSafeTriangle, setNavigationItemSafeTriangle] = useState(() => undefined);
|
|
20
|
+
const [subNavigationId, setSubNavigationId] = useState(() => undefined);
|
|
21
|
+
const [subNavigationToggleId, setSubNavigationToggleId] = useState(() => undefined);
|
|
18
22
|
function handleNavigationItemClick(event) {
|
|
19
23
|
var _a;
|
|
20
24
|
if (((_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.nodeName) === "A") {
|
|
@@ -38,7 +42,11 @@ function DBNavigationItemFn(props, component) {
|
|
|
38
42
|
setIsSubNavigationExpanded(false);
|
|
39
43
|
}
|
|
40
44
|
useEffect(() => {
|
|
45
|
+
var _a;
|
|
41
46
|
setInitialized(true);
|
|
47
|
+
const subNavId = `sub-nav-${(_a = props.id) !== null && _a !== void 0 ? _a : uuid}`;
|
|
48
|
+
setSubNavigationId(subNavId);
|
|
49
|
+
setSubNavigationToggleId(`${subNavId}-toggle`);
|
|
42
50
|
}, []);
|
|
43
51
|
useEffect(() => {
|
|
44
52
|
if (props.subNavigationExpanded !== undefined) {
|
|
@@ -65,8 +73,8 @@ function DBNavigationItemFn(props, component) {
|
|
|
65
73
|
return (React.createElement("li", Object.assign({ ref: _ref }, filterPassingProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), { id: (_a = props.id) !== null && _a !== void 0 ? _a : (_b = props.propOverrides) === null || _b === void 0 ? void 0 : _b.id, onMouseOver: (event) => navigationItemSafeTriangle === null || navigationItemSafeTriangle === void 0 ? void 0 : navigationItemSafeTriangle.enableFollow(), onMouseLeave: (event) => navigationItemSafeTriangle === null || navigationItemSafeTriangle === void 0 ? void 0 : navigationItemSafeTriangle.disableFollow(), onMouseMove: (event) => navigationItemSafeTriangle === null || navigationItemSafeTriangle === void 0 ? void 0 : navigationItemSafeTriangle.followByMouseEvent(event) }, getRootProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), { className: cls("db-navigation-item", props.className), "data-width": props.width, "data-icon": props.icon, "data-show-icon": getBooleanAsString(props.showIcon), "data-active": props.active, "data-wrap": getBooleanAsString(props.wrap), "aria-disabled": getBooleanAsString(props.disabled) }),
|
|
66
74
|
!hasSubNavigation ? (React.createElement(React.Fragment, null, props.text ? React.createElement(React.Fragment, null, props.text) : React.createElement(React.Fragment, null, props.children))) : null,
|
|
67
75
|
hasSubNavigation ? (React.createElement(React.Fragment, null,
|
|
68
|
-
React.createElement("button", { className: "db-navigation-item-expand-button", "aria-haspopup": hasAreaPopup, "aria-expanded": isSubNavigationExpanded, disabled: getBoolean(props.disabled, "disabled"), onClick: (event) => handleClick(event) }, props.text ? React.createElement(React.Fragment, null, props.text) : React.createElement(React.Fragment, null, props.children)),
|
|
69
|
-
React.createElement("menu", { className: "db-sub-navigation", "data-force-close": autoClose, onClick: (event) => handleNavigationItemClick(event) },
|
|
76
|
+
React.createElement("button", { className: "db-navigation-item-expand-button", id: subNavigationToggleId, "aria-haspopup": hasAreaPopup ? "true" : undefined, "aria-expanded": isSubNavigationExpanded, "aria-controls": subNavigationId, disabled: getBoolean(props.disabled, "disabled"), onClick: (event) => handleClick(event) }, props.text ? React.createElement(React.Fragment, null, props.text) : React.createElement(React.Fragment, null, props.children)),
|
|
77
|
+
React.createElement("menu", { className: "db-sub-navigation", id: subNavigationId, "aria-labelledby": subNavigationToggleId, "data-force-close": autoClose, onClick: (event) => handleNavigationItemClick(event) },
|
|
70
78
|
hasAreaPopup ? (React.createElement("div", { className: "db-mobile-navigation-back" },
|
|
71
79
|
React.createElement(DBButton, { icon: "arrow_left", variant: "ghost", id: props.backButtonId, onClick: (event) => handleBackClick(event) }, (_c = props.backButtonText) !== null && _c !== void 0 ? _c : DEFAULT_BACK))) : null,
|
|
72
80
|
React.createElement(React.Fragment, null, props.subNavigation)))) : null));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the framework-specific Storybook URL for the current component page.
|
|
3
|
+
* @param currentUrl - Current page URL
|
|
4
|
+
* @param frameworkRaw - Framework name ('react', 'angular', 'vue', 'html')
|
|
5
|
+
* @returns Storybook URL or undefined when the component route cannot be determined
|
|
6
|
+
*/
|
|
7
|
+
export declare function getShowCodeHref(currentUrl: string, frameworkRaw: string | null | undefined): string | undefined;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const LOCALHOST_STORYBOOK_PORT_MAP = {
|
|
2
|
+
react: '6005',
|
|
3
|
+
angular: '6006',
|
|
4
|
+
vue: '6007',
|
|
5
|
+
html: '6001'
|
|
6
|
+
};
|
|
7
|
+
const DEFAULT_FRAMEWORK = 'react';
|
|
8
|
+
const NON_COMPONENT_ROUTE_SEGMENTS = new Set(['docs', 'overview']);
|
|
9
|
+
/**
|
|
10
|
+
* Builds the Storybook docs path for a component route.
|
|
11
|
+
* @param pathname - URL pathname (e.g., "/docs/components/my-button")
|
|
12
|
+
* @returns Storybook docs path or undefined when no component segment is available
|
|
13
|
+
*/
|
|
14
|
+
function buildStorybookDocsPathname(pathname) {
|
|
15
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
16
|
+
const componentsIndex = segments.indexOf('components');
|
|
17
|
+
const routeSegments = segments.slice(componentsIndex + 1);
|
|
18
|
+
if (componentsIndex === -1 || routeSegments.length === 0) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
let componentName = routeSegments.at(-1);
|
|
22
|
+
if (componentName && NON_COMPONENT_ROUTE_SEGMENTS.has(componentName) && routeSegments.length > 1) {
|
|
23
|
+
componentName = routeSegments.at(-2);
|
|
24
|
+
}
|
|
25
|
+
if (!componentName) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const normalizedComponentName = componentName.replace(/-/g, '');
|
|
29
|
+
return `/docs/components-db${normalizedComponentName}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolves the framework-specific Storybook URL for the current component page.
|
|
33
|
+
* @param currentUrl - Current page URL
|
|
34
|
+
* @param frameworkRaw - Framework name ('react', 'angular', 'vue', 'html')
|
|
35
|
+
* @returns Storybook URL or undefined when the component route cannot be determined
|
|
36
|
+
*/
|
|
37
|
+
export function getShowCodeHref(currentUrl, frameworkRaw) {
|
|
38
|
+
const framework = (frameworkRaw || DEFAULT_FRAMEWORK).toLowerCase();
|
|
39
|
+
const url = new URL(currentUrl);
|
|
40
|
+
const componentsIndex = url.pathname.indexOf('/components/');
|
|
41
|
+
const docsPath = buildStorybookDocsPathname(url.pathname);
|
|
42
|
+
if (componentsIndex === -1 || !docsPath) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
|
|
46
|
+
const port = LOCALHOST_STORYBOOK_PORT_MAP[framework] || LOCALHOST_STORYBOOK_PORT_MAP[DEFAULT_FRAMEWORK];
|
|
47
|
+
return `${url.protocol}//${url.hostname}:${port}/?path=${docsPath}`;
|
|
48
|
+
}
|
|
49
|
+
const basePath = url.pathname.slice(0, componentsIndex);
|
|
50
|
+
return `${url.origin}${basePath}/${framework}-storybook?path=${docsPath}`;
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { getShowCodeHref } from '../shared/showcase/show-code-link';
|
|
3
|
+
describe('getShowCodeHref', () => {
|
|
4
|
+
it('returns localhost react storybook url on localhost', () => {
|
|
5
|
+
expect(getShowCodeHref('http://localhost:3000/core-web/sub/components/action/button/overview', 'react')).toBe('http://localhost:6005/?path=/docs/components-dbbutton');
|
|
6
|
+
});
|
|
7
|
+
it('returns localhost angular storybook url on localhost', () => {
|
|
8
|
+
expect(getShowCodeHref('http://localhost:3000/core-web/sub/components/action/button/overview', 'angular')).toBe('http://localhost:6006/?path=/docs/components-dbbutton');
|
|
9
|
+
});
|
|
10
|
+
it('returns localhost vue storybook url on localhost', () => {
|
|
11
|
+
expect(getShowCodeHref('http://127.0.0.1:3000/core-web/sub/components/action/button/overview', 'vue')).toBe('http://127.0.0.1:6007/?path=/docs/components-dbbutton');
|
|
12
|
+
});
|
|
13
|
+
it('returns localhost html storybook url on localhost', () => {
|
|
14
|
+
expect(getShowCodeHref('http://localhost:3000/core-web/sub/components/action/button/overview', 'html')).toBe('http://localhost:6001/?path=/docs/components-dbbutton');
|
|
15
|
+
});
|
|
16
|
+
it('returns localhost url for direct docs route', () => {
|
|
17
|
+
expect(getShowCodeHref('http://localhost:3000/core-web/components/button/docs', 'react')).toBe('http://localhost:6005/?path=/docs/components-dbbutton');
|
|
18
|
+
});
|
|
19
|
+
it('returns production url for non-localhost', () => {
|
|
20
|
+
expect(getShowCodeHref('https://db-ui.com/core-web/sub/components/action/button/overview', 'react')).toBe('https://db-ui.com/core-web/sub/react-storybook?path=/docs/components-dbbutton');
|
|
21
|
+
});
|
|
22
|
+
it('returns undefined if url has no components segment', () => {
|
|
23
|
+
expect(getShowCodeHref('https://db-ui.com/core-web/sub/', 'react')).toBe(undefined);
|
|
24
|
+
});
|
|
25
|
+
it('returns undefined for components root route', () => {
|
|
26
|
+
expect(getShowCodeHref('https://db-ui.com/core-web/components', 'react')).toBe(undefined);
|
|
27
|
+
});
|
|
28
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@db-ux/react-core-components",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.4",
|
|
4
4
|
"description": "React components for @db-ux/core-components",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"sideEffects": false,
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@db-ux/core-components": "4.5.
|
|
45
|
-
"@db-ux/core-foundations": "4.5.
|
|
44
|
+
"@db-ux/core-components": "4.5.4",
|
|
45
|
+
"@db-ux/core-foundations": "4.5.4"
|
|
46
46
|
}
|
|
47
47
|
}
|