From dcac6b3f227116fa8444883971d1625850fe56f9 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 17 Jun 2020 14:49:54 +0200 Subject: [PATCH] fixed active state of sub navigation items, which are using activeWhenMatch --- CHANGELOG.md | 1 + .../src/__snapshots__/storyshots.test.ts.snap | 62 +++++++++++++++++++ .../ui-components/src/navigation/NavLink.tsx | 10 ++- .../SecondaryNavigation.stories.tsx | 16 ++++- .../src/navigation/SubNavigation.tsx | 24 +++---- .../src/navigation/useActiveMatch.ts | 48 ++++++++++++++ 6 files changed, 143 insertions(+), 18 deletions(-) create mode 100644 scm-ui/ui-components/src/navigation/useActiveMatch.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a0bd842452..dc52916fa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Close file lists in migration ([#1191](https://github.com/scm-manager/scm-manager/pull/1191)) - Use command in javahg.py from registrar (Upgrade to newer javahg version) ([#1192](https://github.com/scm-manager/scm-manager/pull/1192)) - Fixed wrong e-tag format ([sdorra/web-resource #1](https://github.com/sdorra/web-resources/pull/1)) +- Fixed active state of sub navigation items, which are using activeWhenMatch - Handles repositories in custom directories correctly in migration from 1.x ([#1201](https://github.com/scm-manager/scm-manager/pull/1201)) - Usage of short git commit ids in changeset urls ([#1200](https://github.com/scm-manager/scm-manager/pull/1200)) diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index 4be4a8a349..4ab1158583 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -40777,6 +40777,68 @@ exports[`Storyshots Modal|Modal Default 1`] = ` `; +exports[`Storyshots Navigation|Secondary Active when match 1`] = ` +
+
+ +
+
+`; + exports[`Storyshots Navigation|Secondary Default 1`] = `
= ({ to, activeOnlyWhenExact, icon, label, title }) => { - const match = useRouteMatch({ - path: to, - exact: activeOnlyWhenExact - }); +const NavLink: FC = ({ to, activeWhenMatch, activeOnlyWhenExact, icon, label, title }) => { + const active = useActiveMatch({to, activeWhenMatch, activeOnlyWhenExact}); const context = useMenuContext(); const collapsed = context.isCollapsed(); @@ -54,7 +52,7 @@ const NavLink: FC = ({ to, activeOnlyWhenExact, icon, label, title }) => return (
  • - + {showIcon} {collapsed ? null : label} diff --git a/scm-ui/ui-components/src/navigation/SecondaryNavigation.stories.tsx b/scm-ui/ui-components/src/navigation/SecondaryNavigation.stories.tsx index 73531dcb18..715e566cd2 100644 --- a/scm-ui/ui-components/src/navigation/SecondaryNavigation.stories.tsx +++ b/scm-ui/ui-components/src/navigation/SecondaryNavigation.stories.tsx @@ -86,4 +86,18 @@ storiesOf("Navigation|Secondary", module) ); - }); + }) + .add("Active when match", () => + withRoute("/hog")( + + + route.location.pathname === "/hog"} + to="/heart-of-gold" + icon="fas fa-star" + label="Heart Of Gold" + title="Heart Of Gold" + /> + + ) + ); diff --git a/scm-ui/ui-components/src/navigation/SubNavigation.tsx b/scm-ui/ui-components/src/navigation/SubNavigation.tsx index 56a6c438d3..1e831536ee 100644 --- a/scm-ui/ui-components/src/navigation/SubNavigation.tsx +++ b/scm-ui/ui-components/src/navigation/SubNavigation.tsx @@ -22,10 +22,11 @@ * SOFTWARE. */ import React, { FC, useContext } from "react"; -import { Link, useRouteMatch } from "react-router-dom"; +import { Link, useRouteMatch, useLocation } from "react-router-dom"; import classNames from "classnames"; import useMenuContext, { MenuContext } from "./MenuContext"; import { RoutingProps } from "./RoutingProps"; +import useActiveMatch from "./useActiveMatch"; type Props = RoutingProps & { label: string; @@ -33,18 +34,19 @@ type Props = RoutingProps & { icon?: string; }; -const SubNavigation: FC = ({ to, activeOnlyWhenExact, icon, title, label, children }) => { +const SubNavigation: FC = ({ to, activeOnlyWhenExact, activeWhenMatch, icon, title, label, children }) => { + const context = useMenuContext(); + const collapsed = context.isCollapsed(); + const parents = to.split("/"); parents.splice(-1, 1); const parent = parents.join("/"); - const match = useRouteMatch({ - path: parent, - exact: activeOnlyWhenExact - }); - - const context = useMenuContext(); - const collapsed = context.isCollapsed(); + const active = useActiveMatch({ + to: parent, + activeOnlyWhenExact, + activeWhenMatch + }) let defaultIcon = "fas fa-cog"; if (icon) { @@ -52,13 +54,13 @@ const SubNavigation: FC = ({ to, activeOnlyWhenExact, icon, title, label, } let childrenList = null; - if (match && !collapsed) { + if (active && !collapsed) { childrenList =
      {children}
    ; } return (
  • - + {collapsed ? "" : label} {childrenList} diff --git a/scm-ui/ui-components/src/navigation/useActiveMatch.ts b/scm-ui/ui-components/src/navigation/useActiveMatch.ts new file mode 100644 index 0000000000..1ff949d8aa --- /dev/null +++ b/scm-ui/ui-components/src/navigation/useActiveMatch.ts @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import {useLocation, useRouteMatch} from "react-router-dom"; +import {RoutingProps} from "./RoutingProps"; + +const useActiveMatch = ({to, activeOnlyWhenExact, activeWhenMatch}: RoutingProps) => { + const match = useRouteMatch({ + path: to, + exact: activeOnlyWhenExact + }); + + const location = useLocation(); + + const isActiveWhenMatch = () => { + if (activeWhenMatch) { + return activeWhenMatch({ + location + }); + } + return false; + }; + + return !!match || isActiveWhenMatch(); +}; + +export default useActiveMatch;