@scm-manager/ui-components 3.7.2-20250119-163935 → 3.7.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.
- package/package.json +13 -13
- package/src/__snapshots__/storyshots.test.ts.snap +4 -11
- package/src/layout/SecondaryNavigationColumn.tsx +1 -10
- package/src/navigation/ExternalNavLink.tsx +4 -2
- package/src/navigation/NavLink.tsx +11 -5
- package/src/navigation/SecondaryNavigation.stories.tsx +4 -7
- package/src/navigation/SecondaryNavigation.tsx +12 -3
- package/src/navigation/SecondaryNavigationContext.ts +19 -0
- package/src/useSecondaryNavigation.tsx +17 -11
- package/src/navigation/SecondaryNavigationContext.tsx +0 -37
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scm-manager/ui-components",
|
|
3
|
-
"version": "3.7.2
|
|
3
|
+
"version": "3.7.2",
|
|
4
4
|
"description": "UI Components for SCM-Manager and its plugins",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"files": [
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"react-query": "^3.39.2"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@scm-manager/ui-tests": "3.7.2
|
|
36
|
-
"@scm-manager/ui-types": "3.7.2
|
|
35
|
+
"@scm-manager/ui-tests": "3.7.2",
|
|
36
|
+
"@scm-manager/ui-types": "3.7.2",
|
|
37
37
|
"@types/fetch-mock": "^7.3.1",
|
|
38
38
|
"@types/react-select": "^2.0.19",
|
|
39
39
|
"@types/unist": "^2.0.3",
|
|
40
40
|
"gitdiff-parser": "^0.2.2",
|
|
41
41
|
"i18next-fetch-backend": "4",
|
|
42
|
-
"webpack": "^5.
|
|
42
|
+
"webpack": "^5.72.0",
|
|
43
43
|
"@storybook/addon-actions": "^6.5.10",
|
|
44
44
|
"@storybook/addon-essentials": "^6.5.10",
|
|
45
45
|
"@storybook/addon-interactions": "^6.5.10",
|
|
@@ -67,17 +67,17 @@
|
|
|
67
67
|
"@scm-manager/jest-preset": "^2.14.1",
|
|
68
68
|
"@scm-manager/prettier-config": "^2.12.0",
|
|
69
69
|
"@scm-manager/tsconfig": "^2.13.0",
|
|
70
|
-
"@scm-manager/ui-syntaxhighlighting": "3.7.2
|
|
71
|
-
"@scm-manager/ui-shortcuts": "3.7.2
|
|
72
|
-
"@scm-manager/ui-text": "3.7.2
|
|
70
|
+
"@scm-manager/ui-syntaxhighlighting": "3.7.2",
|
|
71
|
+
"@scm-manager/ui-shortcuts": "3.7.2",
|
|
72
|
+
"@scm-manager/ui-text": "3.7.2"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@scm-manager/ui-core": "3.7.2
|
|
76
|
-
"@scm-manager/ui-overlays": "3.7.2
|
|
77
|
-
"@scm-manager/ui-layout": "3.7.2
|
|
78
|
-
"@scm-manager/ui-buttons": "3.7.2
|
|
79
|
-
"@scm-manager/ui-api": "3.7.2
|
|
80
|
-
"@scm-manager/ui-extensions": "3.7.2
|
|
75
|
+
"@scm-manager/ui-core": "3.7.2",
|
|
76
|
+
"@scm-manager/ui-overlays": "3.7.2",
|
|
77
|
+
"@scm-manager/ui-layout": "3.7.2",
|
|
78
|
+
"@scm-manager/ui-buttons": "3.7.2",
|
|
79
|
+
"@scm-manager/ui-api": "3.7.2",
|
|
80
|
+
"@scm-manager/ui-extensions": "3.7.2",
|
|
81
81
|
"deepmerge": "^4.2.2",
|
|
82
82
|
"hast-util-sanitize": "^3.0.2",
|
|
83
83
|
"react-diff-view": "^2.4.10",
|
|
@@ -78158,18 +78158,11 @@ exports[`Storyshots Secondary Navigation Active when match 1`] = `
|
|
|
78158
78158
|
<div>
|
|
78159
78159
|
<button
|
|
78160
78160
|
aria-label="secondaryNavigation.hideContent"
|
|
78161
|
-
className="button SecondaryNavigation__MenuButton-sc-8p1rgi-2 fkeiWf menu-label"
|
|
78161
|
+
className="button SecondaryNavigation__MenuButton-sc-8p1rgi-2 fkeiWf menu-label is-clickable"
|
|
78162
78162
|
collapsed={false}
|
|
78163
78163
|
onClick={[Function]}
|
|
78164
78164
|
type="button"
|
|
78165
78165
|
>
|
|
78166
|
-
<i
|
|
78167
|
-
className="SecondaryNavigation__Icon-sc-8p1rgi-1 gqxbcY is-medium"
|
|
78168
|
-
>
|
|
78169
|
-
<i
|
|
78170
|
-
className="fas fa-caret-down"
|
|
78171
|
-
/>
|
|
78172
|
-
</i>
|
|
78173
78166
|
Hitchhiker
|
|
78174
78167
|
</button>
|
|
78175
78168
|
<ul
|
|
@@ -78222,7 +78215,7 @@ exports[`Storyshots Secondary Navigation Default 1`] = `
|
|
|
78222
78215
|
<div>
|
|
78223
78216
|
<button
|
|
78224
78217
|
aria-label="secondaryNavigation.hideContent"
|
|
78225
|
-
className="button SecondaryNavigation__MenuButton-sc-8p1rgi-2 fkeiWf menu-label"
|
|
78218
|
+
className="button SecondaryNavigation__MenuButton-sc-8p1rgi-2 fkeiWf menu-label is-clickable"
|
|
78226
78219
|
collapsed={false}
|
|
78227
78220
|
onClick={[Function]}
|
|
78228
78221
|
type="button"
|
|
@@ -78285,7 +78278,7 @@ exports[`Storyshots Secondary Navigation Extension Point 1`] = `
|
|
|
78285
78278
|
<div>
|
|
78286
78279
|
<button
|
|
78287
78280
|
aria-label="secondaryNavigation.hideContent"
|
|
78288
|
-
className="button SecondaryNavigation__MenuButton-sc-8p1rgi-2 fkeiWf menu-label"
|
|
78281
|
+
className="button SecondaryNavigation__MenuButton-sc-8p1rgi-2 fkeiWf menu-label is-clickable"
|
|
78289
78282
|
collapsed={false}
|
|
78290
78283
|
onClick={[Function]}
|
|
78291
78284
|
type="button"
|
|
@@ -78376,7 +78369,7 @@ exports[`Storyshots Secondary Navigation Sub Navigation 1`] = `
|
|
|
78376
78369
|
<div>
|
|
78377
78370
|
<button
|
|
78378
78371
|
aria-label="secondaryNavigation.hideContent"
|
|
78379
|
-
className="button SecondaryNavigation__MenuButton-sc-8p1rgi-2 fkeiWf menu-label"
|
|
78372
|
+
className="button SecondaryNavigation__MenuButton-sc-8p1rgi-2 fkeiWf menu-label is-clickable"
|
|
78380
78373
|
collapsed={false}
|
|
78381
78374
|
onClick={[Function]}
|
|
78382
78375
|
type="button"
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
import React, { FC } from "react";
|
|
18
18
|
import styled from "styled-components";
|
|
19
19
|
import { useSecondaryNavigation } from "../useSecondaryNavigation";
|
|
20
|
-
import { SecondaryNavigationProvider } from "../navigation/SecondaryNavigationContext";
|
|
21
20
|
|
|
22
21
|
const SecondaryColumn = styled.div<{ collapsed: boolean }>`
|
|
23
22
|
flex: 0 0 auto;
|
|
@@ -29,7 +28,7 @@ const SecondaryColumn = styled.div<{ collapsed: boolean }>`
|
|
|
29
28
|
}
|
|
30
29
|
`;
|
|
31
30
|
|
|
32
|
-
const
|
|
31
|
+
const SecondaryNavigationColumn: FC = ({ children }) => {
|
|
33
32
|
const { collapsed } = useSecondaryNavigation();
|
|
34
33
|
|
|
35
34
|
return (
|
|
@@ -39,12 +38,4 @@ const SecondaryNavigationColumnIntern: FC = ({ children }) => {
|
|
|
39
38
|
);
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
const SecondaryNavigationColumn: FC = ({ children }) => {
|
|
43
|
-
return (
|
|
44
|
-
<SecondaryNavigationProvider>
|
|
45
|
-
<SecondaryNavigationColumnIntern>{children}</SecondaryNavigationColumnIntern>
|
|
46
|
-
</SecondaryNavigationProvider>
|
|
47
|
-
);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
41
|
export default SecondaryNavigationColumn;
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
* along with this program. If not, see https://www.gnu.org/licenses/.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import React, { FC } from "react";
|
|
17
|
+
import React, { FC, useContext } from "react";
|
|
18
18
|
import classNames from "classnames";
|
|
19
19
|
import { useSecondaryNavigation } from "../useSecondaryNavigation";
|
|
20
20
|
import ExternalLink from "./ExternalLink";
|
|
21
|
+
import { SecondaryNavigationContext } from "./SecondaryNavigationContext";
|
|
21
22
|
|
|
22
23
|
type Props = {
|
|
23
24
|
to: string;
|
|
@@ -27,6 +28,7 @@ type Props = {
|
|
|
27
28
|
|
|
28
29
|
const ExternalNavLink: FC<Props> = ({ to, icon, label }) => {
|
|
29
30
|
const { collapsed } = useSecondaryNavigation();
|
|
31
|
+
const isSecondaryNavigation = useContext(SecondaryNavigationContext);
|
|
30
32
|
|
|
31
33
|
let showIcon;
|
|
32
34
|
if (icon) {
|
|
@@ -41,7 +43,7 @@ const ExternalNavLink: FC<Props> = ({ to, icon, label }) => {
|
|
|
41
43
|
<li title={collapsed ? label : undefined}>
|
|
42
44
|
<ExternalLink to={to} className={collapsed ? "has-text-centered" : ""} aria-label={collapsed ? label : undefined}>
|
|
43
45
|
{showIcon}
|
|
44
|
-
{collapsed ? null : label}
|
|
46
|
+
{isSecondaryNavigation && collapsed ? null : label}
|
|
45
47
|
</ExternalLink>
|
|
46
48
|
</li>
|
|
47
49
|
);
|
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
import React, { FC, useContext, useEffect } from "react";
|
|
18
18
|
import classNames from "classnames";
|
|
19
19
|
import { Link } from "react-router-dom";
|
|
20
|
-
import { createAttributesForTesting } from "@scm-manager/ui-core";
|
|
21
20
|
import { useSecondaryNavigation } from "../useSecondaryNavigation";
|
|
22
21
|
import { RoutingProps } from "./RoutingProps";
|
|
23
|
-
import { SubNavigationContext } from "./SubNavigationContext";
|
|
24
22
|
import useActiveMatch from "./useActiveMatch";
|
|
23
|
+
import { createAttributesForTesting } from "@scm-manager/ui-core";
|
|
24
|
+
import { SecondaryNavigationContext } from "./SecondaryNavigationContext";
|
|
25
|
+
import { SubNavigationContext } from "./SubNavigationContext";
|
|
25
26
|
|
|
26
27
|
type Props = RoutingProps & {
|
|
27
28
|
label: string;
|
|
@@ -50,13 +51,14 @@ const NavLinkContent: FC<NavLinkContentProp> = ({ label, icon, collapsed }) => (
|
|
|
50
51
|
const NavLink: FC<Props> = ({ to, activeWhenMatch, activeOnlyWhenExact, title, testId, children, ...contentProps }) => {
|
|
51
52
|
const active = useActiveMatch({ to, activeWhenMatch, activeOnlyWhenExact });
|
|
52
53
|
const { collapsed, setCollapsible } = useSecondaryNavigation();
|
|
54
|
+
const isSecondaryNavigation = useContext(SecondaryNavigationContext);
|
|
53
55
|
const isSubNavigation = useContext(SubNavigationContext);
|
|
54
56
|
|
|
55
57
|
useEffect(() => {
|
|
56
|
-
if (active) {
|
|
58
|
+
if (isSecondaryNavigation && active) {
|
|
57
59
|
setCollapsible(!isSubNavigation);
|
|
58
60
|
}
|
|
59
|
-
}, [active, isSubNavigation, setCollapsible]);
|
|
61
|
+
}, [active, isSecondaryNavigation, isSubNavigation, setCollapsible]);
|
|
60
62
|
|
|
61
63
|
return (
|
|
62
64
|
<li title={collapsed ? title : undefined}>
|
|
@@ -67,7 +69,11 @@ const NavLink: FC<Props> = ({ to, activeWhenMatch, activeOnlyWhenExact, title, t
|
|
|
67
69
|
aria-label={collapsed ? title : undefined}
|
|
68
70
|
{...(active ? { "aria-current": "page" } : {})}
|
|
69
71
|
>
|
|
70
|
-
{children
|
|
72
|
+
{children ? (
|
|
73
|
+
children
|
|
74
|
+
) : (
|
|
75
|
+
<NavLinkContent {...contentProps} collapsed={(isSecondaryNavigation && collapsed) ?? false} />
|
|
76
|
+
)}
|
|
71
77
|
</Link>
|
|
72
78
|
</li>
|
|
73
79
|
);
|
|
@@ -16,13 +16,12 @@
|
|
|
16
16
|
|
|
17
17
|
import { storiesOf } from "@storybook/react";
|
|
18
18
|
import React, { ReactElement } from "react";
|
|
19
|
-
import { MemoryRouter } from "react-router-dom";
|
|
20
|
-
import styled from "styled-components";
|
|
21
|
-
import { Binder, ExtensionPoint, BinderContext } from "@scm-manager/ui-extensions";
|
|
22
|
-
import { SecondaryNavigationProvider } from "./SecondaryNavigationContext";
|
|
23
19
|
import SecondaryNavigation from "./SecondaryNavigation";
|
|
24
20
|
import SecondaryNavigationItem from "./SecondaryNavigationItem";
|
|
21
|
+
import styled from "styled-components";
|
|
25
22
|
import SubNavigation from "./SubNavigation";
|
|
23
|
+
import { Binder, ExtensionPoint, BinderContext } from "@scm-manager/ui-extensions";
|
|
24
|
+
import { MemoryRouter } from "react-router-dom";
|
|
26
25
|
|
|
27
26
|
const Columns = styled.div`
|
|
28
27
|
margin: 2rem;
|
|
@@ -47,9 +46,7 @@ const withRoute = (route: string) => {
|
|
|
47
46
|
storiesOf("Secondary Navigation", module)
|
|
48
47
|
.addDecorator((story) => (
|
|
49
48
|
<Columns className="columns">
|
|
50
|
-
<div className="column is-3">
|
|
51
|
-
<SecondaryNavigationProvider>{story()}</SecondaryNavigationProvider>
|
|
52
|
-
</div>
|
|
49
|
+
<div className="column is-3">{story()}</div>
|
|
53
50
|
</Columns>
|
|
54
51
|
))
|
|
55
52
|
.add("Default", () =>
|
|
@@ -16,8 +16,10 @@
|
|
|
16
16
|
|
|
17
17
|
import React, { FC } from "react";
|
|
18
18
|
import styled from "styled-components";
|
|
19
|
+
import classNames from "classnames";
|
|
19
20
|
import { useTranslation } from "react-i18next";
|
|
20
21
|
import { useSecondaryNavigation } from "../useSecondaryNavigation";
|
|
22
|
+
import { SecondaryNavigationContext } from "./SecondaryNavigationContext";
|
|
21
23
|
import { Button } from "@scm-manager/ui-buttons";
|
|
22
24
|
|
|
23
25
|
type Props = {
|
|
@@ -71,9 +73,14 @@ const SecondaryNavigation: FC<Props> = ({ label, children, collapsible = true })
|
|
|
71
73
|
const menuAriaLabel = collapsed ? t("secondaryNavigation.showContent") : t("secondaryNavigation.hideContent");
|
|
72
74
|
|
|
73
75
|
return (
|
|
74
|
-
<SectionContainer className="menu" collapsed={collapsed}>
|
|
76
|
+
<SectionContainer className="menu" collapsed={collapsed ?? false}>
|
|
75
77
|
<div>
|
|
76
|
-
<MenuButton
|
|
78
|
+
<MenuButton
|
|
79
|
+
className={classNames("menu-label", { "is-clickable": true })}
|
|
80
|
+
collapsed={collapsed}
|
|
81
|
+
onClick={toggleCollapse}
|
|
82
|
+
aria-label={menuAriaLabel}
|
|
83
|
+
>
|
|
77
84
|
{isCollapsible ? (
|
|
78
85
|
<Icon className="is-medium" collapsed={collapsed}>
|
|
79
86
|
{arrowIcon}
|
|
@@ -81,7 +88,9 @@ const SecondaryNavigation: FC<Props> = ({ label, children, collapsible = true })
|
|
|
81
88
|
) : null}
|
|
82
89
|
{collapsed ? "" : label}
|
|
83
90
|
</MenuButton>
|
|
84
|
-
<ul className="menu-list">
|
|
91
|
+
<ul className="menu-list">
|
|
92
|
+
<SecondaryNavigationContext.Provider value={true}>{children}</SecondaryNavigationContext.Provider>
|
|
93
|
+
</ul>
|
|
85
94
|
</div>
|
|
86
95
|
</SectionContainer>
|
|
87
96
|
);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2020 - present Cloudogu GmbH
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
* the terms of the GNU Affero General Public License as published by the Free
|
|
6
|
+
* Software Foundation, version 3.
|
|
7
|
+
*
|
|
8
|
+
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
9
|
+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
10
|
+
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
11
|
+
* details.
|
|
12
|
+
*
|
|
13
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
14
|
+
* along with this program. If not, see https://www.gnu.org/licenses/.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import React from "react";
|
|
18
|
+
|
|
19
|
+
export const SecondaryNavigationContext = React.createContext(false);
|
|
@@ -15,14 +15,17 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { useLocalStorage } from "@scm-manager/ui-api";
|
|
18
|
-
import { useCallback,
|
|
19
|
-
import { SecondaryNavigationContext } from "./navigation/SecondaryNavigationContext";
|
|
18
|
+
import { useCallback, useMemo } from "react";
|
|
20
19
|
|
|
21
20
|
export const useSecondaryNavigation = (isNavigationCollapsible = true) => {
|
|
22
|
-
const
|
|
23
|
-
const [
|
|
21
|
+
const [isCollapsed, setCollapsed] = useLocalStorage<boolean>("secondaryNavigation.collapsed", false);
|
|
22
|
+
const [isRouteCollapsible, setRouteCollapsible] = useLocalStorage<boolean>("secondaryNavigation.collapsible", true);
|
|
24
23
|
|
|
25
|
-
const
|
|
24
|
+
const collapsible = useMemo(
|
|
25
|
+
() => isRouteCollapsible && isNavigationCollapsible,
|
|
26
|
+
[isNavigationCollapsible, isRouteCollapsible]
|
|
27
|
+
);
|
|
28
|
+
const collapsed = useMemo(() => collapsible && isCollapsed, [collapsible, isCollapsed]);
|
|
26
29
|
|
|
27
30
|
const toggleCollapse = useCallback(() => {
|
|
28
31
|
if (collapsible) {
|
|
@@ -30,10 +33,13 @@ export const useSecondaryNavigation = (isNavigationCollapsible = true) => {
|
|
|
30
33
|
}
|
|
31
34
|
}, [collapsible, setCollapsed]);
|
|
32
35
|
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
return useMemo(
|
|
37
|
+
() => ({
|
|
38
|
+
collapsed,
|
|
39
|
+
collapsible,
|
|
40
|
+
setCollapsible: setRouteCollapsible,
|
|
41
|
+
toggleCollapse,
|
|
42
|
+
}),
|
|
43
|
+
[collapsed, collapsible, setRouteCollapsible, toggleCollapse]
|
|
44
|
+
);
|
|
39
45
|
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2020 - present Cloudogu GmbH
|
|
3
|
-
*
|
|
4
|
-
* This program is free software: you can redistribute it and/or modify it under
|
|
5
|
-
* the terms of the GNU Affero General Public License as published by the Free
|
|
6
|
-
* Software Foundation, version 3.
|
|
7
|
-
*
|
|
8
|
-
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
9
|
-
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
10
|
-
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
11
|
-
* details.
|
|
12
|
-
*
|
|
13
|
-
* You should have received a copy of the GNU Affero General Public License
|
|
14
|
-
* along with this program. If not, see https://www.gnu.org/licenses/.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import React, { ReactNode, useMemo, useState } from "react";
|
|
18
|
-
|
|
19
|
-
type SecondaryNavigationContextState = {
|
|
20
|
-
collapsible: boolean;
|
|
21
|
-
setCollapsible: (collapsed: boolean) => void;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const dummy: SecondaryNavigationContextState = {
|
|
25
|
-
collapsible: false,
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
27
|
-
setCollapsible: () => {},
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const SecondaryNavigationContext = React.createContext<SecondaryNavigationContextState>(dummy);
|
|
31
|
-
|
|
32
|
-
export const SecondaryNavigationProvider = ({ children }: { children: ReactNode }) => {
|
|
33
|
-
const [collapsible, setCollapsible] = useState(true);
|
|
34
|
-
const contextValue = useMemo(() => ({ collapsible, setCollapsible }), [collapsible, setCollapsible]);
|
|
35
|
-
|
|
36
|
-
return <SecondaryNavigationContext.Provider value={contextValue}>{children}</SecondaryNavigationContext.Provider>;
|
|
37
|
-
};
|