@scm-manager/ui-components 2.39.1 → 2.39.2-20220912-120120
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 +7 -7
- package/src/__snapshots__/storyshots.test.ts.snap +84 -4
- package/src/layout/Footer.tsx +15 -1
- package/src/layout/Header.tsx +1 -5
- package/src/modals/ActiveModalCountContextProvider.tsx +49 -0
- package/src/modals/ConfirmAlert.stories.tsx +17 -7
- package/src/modals/Modal.stories.tsx +9 -3
- package/src/modals/Modal.tsx +2 -0
- package/src/modals/activeModalCountContext.ts +29 -0
- package/src/modals/index.ts +2 -0
- package/src/modals/useActiveModals.ts +36 -0
- package/src/modals/useRegisterModal.test.tsx +88 -0
- package/src/modals/useRegisterModal.ts +53 -0
- package/src/navigation/SecondaryNavigation.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scm-manager/ui-components",
|
|
3
|
-
"version": "2.39.
|
|
3
|
+
"version": "2.39.2-20220912-120120",
|
|
4
4
|
"description": "UI Components for SCM-Manager and its plugins",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"files": [
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
"update-storyshots": "jest --testPathPattern=\"storyshots.test.ts\" --collectCoverage=false -u"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@scm-manager/ui-syntaxhighlighting": "2.39.
|
|
24
|
-
"@scm-manager/ui-text": "2.39.
|
|
23
|
+
"@scm-manager/ui-syntaxhighlighting": "2.39.2-20220912-120120",
|
|
24
|
+
"@scm-manager/ui-text": "2.39.2-20220912-120120",
|
|
25
25
|
"@scm-manager/babel-preset": "^2.13.1",
|
|
26
26
|
"@scm-manager/eslint-config": "^2.16.0",
|
|
27
27
|
"@scm-manager/jest-preset": "^2.13.0",
|
|
28
28
|
"@scm-manager/prettier-config": "^2.10.1",
|
|
29
29
|
"@scm-manager/tsconfig": "^2.13.0",
|
|
30
|
-
"@scm-manager/ui-tests": "2.39.
|
|
30
|
+
"@scm-manager/ui-tests": "2.39.2-20220912-120120",
|
|
31
31
|
"@storybook/addon-actions": "^6.4.20",
|
|
32
32
|
"@storybook/addon-essentials": "^6.4.20",
|
|
33
33
|
"@storybook/addon-interactions": "^6.4.20",
|
|
@@ -66,9 +66,9 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@headlessui/react": "^1.4.3",
|
|
69
|
-
"@scm-manager/ui-api": "2.39.
|
|
70
|
-
"@scm-manager/ui-extensions": "2.39.
|
|
71
|
-
"@scm-manager/ui-types": "2.39.
|
|
69
|
+
"@scm-manager/ui-api": "2.39.2-20220912-120120",
|
|
70
|
+
"@scm-manager/ui-extensions": "2.39.2-20220912-120120",
|
|
71
|
+
"@scm-manager/ui-types": "2.39.2-20220912-120120",
|
|
72
72
|
"classnames": "^2.2.6",
|
|
73
73
|
"date-fns": "^2.4.1",
|
|
74
74
|
"deepmerge": "^4.2.2",
|
|
@@ -1677,6 +1677,26 @@ exports[`Storyshots Footer Default 1`] = `
|
|
|
1677
1677
|
footer.support.enterprise
|
|
1678
1678
|
</a>
|
|
1679
1679
|
</li>
|
|
1680
|
+
<li>
|
|
1681
|
+
<a
|
|
1682
|
+
className=""
|
|
1683
|
+
href="https://community.cloudogu.com/c/scm-manager/bug-reports-scmm"
|
|
1684
|
+
rel="noopener noreferrer"
|
|
1685
|
+
target="_blank"
|
|
1686
|
+
>
|
|
1687
|
+
footer.support.bugReport
|
|
1688
|
+
</a>
|
|
1689
|
+
</li>
|
|
1690
|
+
<li>
|
|
1691
|
+
<a
|
|
1692
|
+
className=""
|
|
1693
|
+
href="https://community.cloudogu.com/c/scm-manager/feature-requests-scmm"
|
|
1694
|
+
rel="noopener noreferrer"
|
|
1695
|
+
target="_blank"
|
|
1696
|
+
>
|
|
1697
|
+
footer.support.featureRequest
|
|
1698
|
+
</a>
|
|
1699
|
+
</li>
|
|
1680
1700
|
</ul>
|
|
1681
1701
|
</section>
|
|
1682
1702
|
</div>
|
|
@@ -1843,6 +1863,26 @@ exports[`Storyshots Footer Full 1`] = `
|
|
|
1843
1863
|
footer.support.enterprise
|
|
1844
1864
|
</a>
|
|
1845
1865
|
</li>
|
|
1866
|
+
<li>
|
|
1867
|
+
<a
|
|
1868
|
+
className=""
|
|
1869
|
+
href="https://community.cloudogu.com/c/scm-manager/bug-reports-scmm"
|
|
1870
|
+
rel="noopener noreferrer"
|
|
1871
|
+
target="_blank"
|
|
1872
|
+
>
|
|
1873
|
+
footer.support.bugReport
|
|
1874
|
+
</a>
|
|
1875
|
+
</li>
|
|
1876
|
+
<li>
|
|
1877
|
+
<a
|
|
1878
|
+
className=""
|
|
1879
|
+
href="https://community.cloudogu.com/c/scm-manager/feature-requests-scmm"
|
|
1880
|
+
rel="noopener noreferrer"
|
|
1881
|
+
target="_blank"
|
|
1882
|
+
>
|
|
1883
|
+
footer.support.featureRequest
|
|
1884
|
+
</a>
|
|
1885
|
+
</li>
|
|
1846
1886
|
<li>
|
|
1847
1887
|
<a
|
|
1848
1888
|
className=""
|
|
@@ -1990,6 +2030,26 @@ exports[`Storyshots Footer With Avatar 1`] = `
|
|
|
1990
2030
|
footer.support.enterprise
|
|
1991
2031
|
</a>
|
|
1992
2032
|
</li>
|
|
2033
|
+
<li>
|
|
2034
|
+
<a
|
|
2035
|
+
className=""
|
|
2036
|
+
href="https://community.cloudogu.com/c/scm-manager/bug-reports-scmm"
|
|
2037
|
+
rel="noopener noreferrer"
|
|
2038
|
+
target="_blank"
|
|
2039
|
+
>
|
|
2040
|
+
footer.support.bugReport
|
|
2041
|
+
</a>
|
|
2042
|
+
</li>
|
|
2043
|
+
<li>
|
|
2044
|
+
<a
|
|
2045
|
+
className=""
|
|
2046
|
+
href="https://community.cloudogu.com/c/scm-manager/feature-requests-scmm"
|
|
2047
|
+
rel="noopener noreferrer"
|
|
2048
|
+
target="_blank"
|
|
2049
|
+
>
|
|
2050
|
+
footer.support.featureRequest
|
|
2051
|
+
</a>
|
|
2052
|
+
</li>
|
|
1993
2053
|
</ul>
|
|
1994
2054
|
</section>
|
|
1995
2055
|
</div>
|
|
@@ -2148,6 +2208,26 @@ exports[`Storyshots Footer With Plugin Links 1`] = `
|
|
|
2148
2208
|
footer.support.enterprise
|
|
2149
2209
|
</a>
|
|
2150
2210
|
</li>
|
|
2211
|
+
<li>
|
|
2212
|
+
<a
|
|
2213
|
+
className=""
|
|
2214
|
+
href="https://community.cloudogu.com/c/scm-manager/bug-reports-scmm"
|
|
2215
|
+
rel="noopener noreferrer"
|
|
2216
|
+
target="_blank"
|
|
2217
|
+
>
|
|
2218
|
+
footer.support.bugReport
|
|
2219
|
+
</a>
|
|
2220
|
+
</li>
|
|
2221
|
+
<li>
|
|
2222
|
+
<a
|
|
2223
|
+
className=""
|
|
2224
|
+
href="https://community.cloudogu.com/c/scm-manager/feature-requests-scmm"
|
|
2225
|
+
rel="noopener noreferrer"
|
|
2226
|
+
target="_blank"
|
|
2227
|
+
>
|
|
2228
|
+
footer.support.featureRequest
|
|
2229
|
+
</a>
|
|
2230
|
+
</li>
|
|
2151
2231
|
<li>
|
|
2152
2232
|
<a
|
|
2153
2233
|
className=""
|
|
@@ -71378,7 +71458,7 @@ exports[`Storyshots Secondary Navigation Active when match 1`] = `
|
|
|
71378
71458
|
className="column is-3"
|
|
71379
71459
|
>
|
|
71380
71460
|
<aside
|
|
71381
|
-
className="SecondaryNavigation__SectionContainer-sc-8p1rgi-0
|
|
71461
|
+
className="SecondaryNavigation__SectionContainer-sc-8p1rgi-0 cZyrQa menu"
|
|
71382
71462
|
>
|
|
71383
71463
|
<div>
|
|
71384
71464
|
<p
|
|
@@ -71440,7 +71520,7 @@ exports[`Storyshots Secondary Navigation Default 1`] = `
|
|
|
71440
71520
|
className="column is-3"
|
|
71441
71521
|
>
|
|
71442
71522
|
<aside
|
|
71443
|
-
className="SecondaryNavigation__SectionContainer-sc-8p1rgi-0
|
|
71523
|
+
className="SecondaryNavigation__SectionContainer-sc-8p1rgi-0 cZyrQa menu"
|
|
71444
71524
|
>
|
|
71445
71525
|
<div>
|
|
71446
71526
|
<p
|
|
@@ -71502,7 +71582,7 @@ exports[`Storyshots Secondary Navigation Extension Point 1`] = `
|
|
|
71502
71582
|
className="column is-3"
|
|
71503
71583
|
>
|
|
71504
71584
|
<aside
|
|
71505
|
-
className="SecondaryNavigation__SectionContainer-sc-8p1rgi-0
|
|
71585
|
+
className="SecondaryNavigation__SectionContainer-sc-8p1rgi-0 cZyrQa menu"
|
|
71506
71586
|
>
|
|
71507
71587
|
<div>
|
|
71508
71588
|
<p
|
|
@@ -71590,7 +71670,7 @@ exports[`Storyshots Secondary Navigation Sub Navigation 1`] = `
|
|
|
71590
71670
|
className="column is-3"
|
|
71591
71671
|
>
|
|
71592
71672
|
<aside
|
|
71593
|
-
className="SecondaryNavigation__SectionContainer-sc-8p1rgi-0
|
|
71673
|
+
className="SecondaryNavigation__SectionContainer-sc-8p1rgi-0 cZyrQa menu"
|
|
71594
71674
|
>
|
|
71595
71675
|
<div>
|
|
71596
71676
|
<p
|
package/src/layout/Footer.tsx
CHANGED
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
import React, { FC } from "react";
|
|
25
|
-
import { Links, Me } from "@scm-manager/ui-types";
|
|
25
|
+
import { Feedback, Links, Me } from "@scm-manager/ui-types";
|
|
26
|
+
import { useIndex } from "@scm-manager/ui-api";
|
|
26
27
|
import { ExtensionPoint, extensionPoints, useBinder } from "@scm-manager/ui-extensions";
|
|
27
28
|
import { AvatarImage } from "../avatar";
|
|
28
29
|
import NavLink from "../navigation/NavLink";
|
|
@@ -80,6 +81,7 @@ const TitleWithAvatar: FC<TitleWithAvatarProps> = ({ me }) => (
|
|
|
80
81
|
const Footer: FC<Props> = ({ me, version, links }) => {
|
|
81
82
|
const [t] = useTranslation("commons");
|
|
82
83
|
const binder = useBinder();
|
|
84
|
+
const index = useIndex();
|
|
83
85
|
|
|
84
86
|
const extensionProps = { me, url: "/me", links };
|
|
85
87
|
let meSectionTile;
|
|
@@ -124,6 +126,18 @@ const Footer: FC<Props> = ({ me, version, links }) => {
|
|
|
124
126
|
to="https://cloudogu.com/en/scm-manager-enterprise/"
|
|
125
127
|
label={t("footer.support.enterprise")}
|
|
126
128
|
/>
|
|
129
|
+
{!(index.data?._embedded?.feedback as Feedback)?.disabled ? (
|
|
130
|
+
<>
|
|
131
|
+
<ExternalNavLink
|
|
132
|
+
to="https://community.cloudogu.com/c/scm-manager/bug-reports-scmm"
|
|
133
|
+
label={t("footer.support.bugReport")}
|
|
134
|
+
/>
|
|
135
|
+
<ExternalNavLink
|
|
136
|
+
to="https://community.cloudogu.com/c/scm-manager/feature-requests-scmm"
|
|
137
|
+
label={t("footer.support.featureRequest")}
|
|
138
|
+
/>
|
|
139
|
+
</>
|
|
140
|
+
) : null}
|
|
127
141
|
<ExtensionPoint name="footer.support" props={extensionProps} renderAll={true} />
|
|
128
142
|
</FooterSection>
|
|
129
143
|
</div>
|
package/src/layout/Header.tsx
CHANGED
|
@@ -28,10 +28,6 @@ type Props = {
|
|
|
28
28
|
authenticated?: boolean;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
const SmallHeader: FC = ({ children }) => {
|
|
32
|
-
return <div className="has-scm-background">{children}</div>;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
31
|
const LargeHeader: FC = () => {
|
|
36
32
|
return (
|
|
37
33
|
<div className="hero has-scm-background is-small">
|
|
@@ -50,7 +46,7 @@ const LargeHeader: FC = () => {
|
|
|
50
46
|
|
|
51
47
|
const Header: FC<Props> = ({ authenticated, children }) => {
|
|
52
48
|
if (authenticated) {
|
|
53
|
-
return
|
|
49
|
+
return <>{children}</>;
|
|
54
50
|
} else {
|
|
55
51
|
return <LargeHeader />;
|
|
56
52
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import ActiveModalCountContext from "./activeModalCountContext";
|
|
26
|
+
import React, { FC, useCallback, useState } from "react";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A simple counter that allows developers to keep track of how many modals are currently open.
|
|
30
|
+
*
|
|
31
|
+
* A default provider instance wraps the SCM-Manager so there is no need for plugins to use this component.
|
|
32
|
+
*
|
|
33
|
+
* Required by {@link useActiveModals}.
|
|
34
|
+
*/
|
|
35
|
+
const ActiveModalCountContextProvider: FC = ({ children }) => {
|
|
36
|
+
const [activeModalCount, setActiveModalCount] = useState(0);
|
|
37
|
+
const incrementModalCount = useCallback(() => setActiveModalCount((prev) => prev + 1), []);
|
|
38
|
+
const decrementModalCount = useCallback(() => setActiveModalCount((prev) => prev - 1), []);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<ActiveModalCountContext.Provider
|
|
42
|
+
value={{ value: activeModalCount, increment: incrementModalCount, decrement: decrementModalCount }}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
</ActiveModalCountContext.Provider>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default ActiveModalCountContextProvider;
|
|
@@ -26,6 +26,7 @@ import { storiesOf } from "@storybook/react";
|
|
|
26
26
|
import { MemoryRouter } from "react-router-dom";
|
|
27
27
|
import * as React from "react";
|
|
28
28
|
import ConfirmAlert, { confirmAlert } from "./ConfirmAlert";
|
|
29
|
+
import ActiveModalCountContext from "./activeModalCountContext";
|
|
29
30
|
|
|
30
31
|
const body =
|
|
31
32
|
"Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows\n " +
|
|
@@ -37,27 +38,36 @@ const buttons = [
|
|
|
37
38
|
{
|
|
38
39
|
className: "is-outlined",
|
|
39
40
|
label: "Cancel",
|
|
40
|
-
onClick: () => null
|
|
41
|
+
onClick: () => null,
|
|
41
42
|
},
|
|
42
43
|
{
|
|
43
|
-
label: "Submit"
|
|
44
|
-
}
|
|
44
|
+
label: "Submit",
|
|
45
|
+
},
|
|
45
46
|
];
|
|
46
47
|
|
|
47
48
|
const buttonsWithAutofocus = [
|
|
48
49
|
{
|
|
49
50
|
label: "Cancel",
|
|
50
|
-
onClick: () => null
|
|
51
|
+
onClick: () => null,
|
|
51
52
|
},
|
|
52
53
|
{
|
|
53
54
|
className: "is-info",
|
|
54
55
|
label: "I should be focused",
|
|
55
|
-
autofocus: true
|
|
56
|
-
}
|
|
56
|
+
autofocus: true,
|
|
57
|
+
},
|
|
57
58
|
];
|
|
58
59
|
|
|
60
|
+
const doNothing = () => {
|
|
61
|
+
// Do nothing
|
|
62
|
+
};
|
|
63
|
+
|
|
59
64
|
storiesOf("Modal/ConfirmAlert", module)
|
|
60
|
-
.addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
|
65
|
+
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
|
66
|
+
.addDecorator((story) => (
|
|
67
|
+
<ActiveModalCountContext.Provider value={{ value: 0, increment: doNothing, decrement: doNothing }}>
|
|
68
|
+
{story()}
|
|
69
|
+
</ActiveModalCountContext.Provider>
|
|
70
|
+
))
|
|
61
71
|
.add("Default", () => <ConfirmAlert message={body} title={"Are you sure about that?"} buttons={buttons} />)
|
|
62
72
|
.add("WithButton", () => {
|
|
63
73
|
const buttonClick = () => {
|
|
@@ -34,6 +34,7 @@ import { Button, ButtonGroup } from "../buttons";
|
|
|
34
34
|
import Notification from "../Notification";
|
|
35
35
|
import { Autocomplete } from "../index";
|
|
36
36
|
import { SelectValue } from "@scm-manager/ui-types";
|
|
37
|
+
import ActiveModalCountContext from "./activeModalCountContext";
|
|
37
38
|
|
|
38
39
|
const TopAndBottomMargin = styled.div`
|
|
39
40
|
margin: 0.75rem 0; // only for aesthetic reasons
|
|
@@ -75,20 +76,25 @@ const withFormElementsFooter = (
|
|
|
75
76
|
);
|
|
76
77
|
|
|
77
78
|
const loadSuggestions: (p: string) => Promise<SelectValue[]> = () =>
|
|
78
|
-
new Promise(resolve => {
|
|
79
|
+
new Promise((resolve) => {
|
|
79
80
|
setTimeout(() => {
|
|
80
81
|
resolve([
|
|
81
82
|
{ value: { id: "trillian", displayName: "Tricia McMillan" }, label: "Tricia McMillan" },
|
|
82
83
|
{ value: { id: "zaphod", displayName: "Zaphod Beeblebrox" }, label: "Zaphod Beeblebrox" },
|
|
83
84
|
{ value: { id: "ford", displayName: "Ford Prefect" }, label: "Ford Prefect" },
|
|
84
85
|
{ value: { id: "dent", displayName: "Arthur Dent" }, label: "Arthur Dent" },
|
|
85
|
-
{ value: { id: "marvin", displayName: "Marvin" }, label: "Marvin the Paranoid Android " }
|
|
86
|
+
{ value: { id: "marvin", displayName: "Marvin" }, label: "Marvin the Paranoid Android " },
|
|
86
87
|
]);
|
|
87
88
|
});
|
|
88
89
|
});
|
|
89
90
|
|
|
90
91
|
storiesOf("Modal/Modal", module)
|
|
91
|
-
.addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
|
92
|
+
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
|
93
|
+
.addDecorator((story) => (
|
|
94
|
+
<ActiveModalCountContext.Provider value={{ value: 0, increment: doNothing, decrement: doNothing }}>
|
|
95
|
+
{story()}
|
|
96
|
+
</ActiveModalCountContext.Provider>
|
|
97
|
+
))
|
|
92
98
|
.add("Default", () => (
|
|
93
99
|
<NonCloseableModal>
|
|
94
100
|
<p>{text}</p>
|
package/src/modals/Modal.tsx
CHANGED
|
@@ -26,6 +26,7 @@ import classNames from "classnames";
|
|
|
26
26
|
import styled from "styled-components";
|
|
27
27
|
import { Dialog } from "@headlessui/react";
|
|
28
28
|
import { devices } from "../devices";
|
|
29
|
+
import useRegisterModal from "./useRegisterModal";
|
|
29
30
|
|
|
30
31
|
type ModalSize = "S" | "M" | "L";
|
|
31
32
|
|
|
@@ -67,6 +68,7 @@ export const Modal: FC<Props> = ({
|
|
|
67
68
|
initialFocusRef,
|
|
68
69
|
overflowVisible
|
|
69
70
|
}) => {
|
|
71
|
+
useRegisterModal(active);
|
|
70
72
|
const closeButtonRef = useRef<HTMLButtonElement | null>(null);
|
|
71
73
|
let showFooter = null;
|
|
72
74
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import React from "react";
|
|
26
|
+
|
|
27
|
+
export type ModalStateContextType = { value: number; increment: () => void; decrement: () => void };
|
|
28
|
+
|
|
29
|
+
export default React.createContext<ModalStateContextType>({} as ModalStateContextType);
|
package/src/modals/index.ts
CHANGED
|
@@ -27,3 +27,5 @@
|
|
|
27
27
|
export { default as ConfirmAlert, confirmAlert } from "./ConfirmAlert";
|
|
28
28
|
export { default as Modal } from "./Modal";
|
|
29
29
|
export { default as FullscreenModal } from "./FullscreenModal";
|
|
30
|
+
export { default as ActiveModalCountContextProvider } from "./ActiveModalCountContextProvider";
|
|
31
|
+
export { default as useActiveModals } from "./useActiveModals";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { useContext } from "react";
|
|
26
|
+
import ActiveModalCountContext from "./activeModalCountContext";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @returns Whether any modals are currently open
|
|
30
|
+
*/
|
|
31
|
+
const useActiveModals = () => {
|
|
32
|
+
const { value } = useContext(ActiveModalCountContext);
|
|
33
|
+
return value > 0;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default useActiveModals;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { renderHook } from "@testing-library/react-hooks";
|
|
26
|
+
import React, { FC } from "react";
|
|
27
|
+
import ActiveModalCountContext, { ModalStateContextType } from "./activeModalCountContext";
|
|
28
|
+
import useRegisterModal from "./useRegisterModal";
|
|
29
|
+
|
|
30
|
+
const createWrapper =
|
|
31
|
+
(context: ModalStateContextType): FC =>
|
|
32
|
+
({ children }) => {
|
|
33
|
+
return <ActiveModalCountContext.Provider value={context}>{children}</ActiveModalCountContext.Provider>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
describe("useRegisterModal", () => {
|
|
37
|
+
it("should increment and not decrement on active registration", () => {
|
|
38
|
+
const increment = jest.fn();
|
|
39
|
+
const decrement = jest.fn();
|
|
40
|
+
|
|
41
|
+
renderHook(() => useRegisterModal(true), {
|
|
42
|
+
wrapper: createWrapper({ value: 0, increment, decrement }),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(decrement).not.toHaveBeenCalled();
|
|
46
|
+
expect(increment).toHaveBeenCalled();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should not decrement on inactive registration", () => {
|
|
50
|
+
const increment = jest.fn();
|
|
51
|
+
const decrement = jest.fn();
|
|
52
|
+
|
|
53
|
+
renderHook(() => useRegisterModal(false), {
|
|
54
|
+
wrapper: createWrapper({ value: 0, increment, decrement }),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(decrement).not.toHaveBeenCalled();
|
|
58
|
+
expect(increment).not.toHaveBeenCalled();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should not decrement on inactive de-registration", () => {
|
|
62
|
+
const increment = jest.fn();
|
|
63
|
+
const decrement = jest.fn();
|
|
64
|
+
|
|
65
|
+
const result = renderHook(() => useRegisterModal(false), {
|
|
66
|
+
wrapper: createWrapper({ value: 0, increment, decrement }),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
result.unmount();
|
|
70
|
+
|
|
71
|
+
expect(decrement).not.toHaveBeenCalled();
|
|
72
|
+
expect(increment).not.toHaveBeenCalled();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should decrement on active de-registration", () => {
|
|
76
|
+
const increment = jest.fn();
|
|
77
|
+
const decrement = jest.fn();
|
|
78
|
+
|
|
79
|
+
const result = renderHook(() => useRegisterModal(false, true), {
|
|
80
|
+
wrapper: createWrapper({ value: 0, increment, decrement }),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
result.unmount();
|
|
84
|
+
|
|
85
|
+
expect(decrement).toHaveBeenCalled();
|
|
86
|
+
expect(increment).not.toHaveBeenCalled();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { useContext, useEffect, useRef } from "react";
|
|
26
|
+
import ActiveModalCount from "./activeModalCountContext";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Should not yet be part of the public API, as it is exclusively used by the {@link Modal} component.
|
|
30
|
+
*
|
|
31
|
+
* @param active Whether the modal is currently open
|
|
32
|
+
* @param initialValue DO NOT USE - Used only for testing purposes
|
|
33
|
+
*/
|
|
34
|
+
export default function useRegisterModal(active: boolean, initialValue: boolean | null = null) {
|
|
35
|
+
const { increment, decrement } = useContext(ActiveModalCount);
|
|
36
|
+
const previousActiveState = useRef<boolean | null>(initialValue);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (active) {
|
|
39
|
+
previousActiveState.current = true;
|
|
40
|
+
increment();
|
|
41
|
+
} else {
|
|
42
|
+
if (previousActiveState.current !== null) {
|
|
43
|
+
decrement();
|
|
44
|
+
}
|
|
45
|
+
previousActiveState.current = false;
|
|
46
|
+
}
|
|
47
|
+
return () => {
|
|
48
|
+
if (previousActiveState.current) {
|
|
49
|
+
decrement();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}, [active, decrement, increment]);
|
|
53
|
+
}
|