From cd6e624e61bc15c947dbb542f6e7e754dfc2b43c Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 7 Jul 2021 16:26:58 +0200 Subject: [PATCH] Small header (#1721) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: René Pfeuffer --- gradle/changelog/redesign_header.yaml | 2 + scm-ui/ui-components/src/Logo.stories.tsx | 16 +- scm-ui/ui-components/src/Logo.tsx | 25 +-- .../src/__snapshots__/storyshots.test.ts.snap | 11 ++ scm-ui/ui-components/src/devices.ts | 12 +- scm-ui/ui-components/src/layout/Header.tsx | 47 +++--- .../src/navigation/PrimaryNavigation.tsx | 112 +++++--------- .../src/navigation/PrimaryNavigationLink.tsx | 45 +++--- scm-ui/ui-styles/package.json | 2 +- scm-ui/ui-styles/src/scm.scss | 12 +- scm-ui/ui-types/src/IndexResources.ts | 3 +- scm-ui/ui-webapp/public/images/scmLogo.svg | 104 +++++++++++++ scm-ui/ui-webapp/src/containers/App.tsx | 15 +- .../src/containers/HeaderActions.tsx | 46 ++++++ .../ui-webapp/src/containers/LoginButton.tsx | 86 +++++++++++ .../ui-webapp/src/containers/LogoutButton.tsx | 86 +++++++++++ scm-ui/ui-webapp/src/containers/Main.tsx | 15 +- .../src/containers/NavigationBar.tsx | 144 ++++++++++++++++++ .../src/containers/Notifications.tsx | 47 ++++-- yarn.lock | 8 +- 20 files changed, 656 insertions(+), 182 deletions(-) create mode 100644 gradle/changelog/redesign_header.yaml create mode 100644 scm-ui/ui-webapp/public/images/scmLogo.svg create mode 100644 scm-ui/ui-webapp/src/containers/HeaderActions.tsx create mode 100644 scm-ui/ui-webapp/src/containers/LoginButton.tsx create mode 100644 scm-ui/ui-webapp/src/containers/LogoutButton.tsx create mode 100644 scm-ui/ui-webapp/src/containers/NavigationBar.tsx diff --git a/gradle/changelog/redesign_header.yaml b/gradle/changelog/redesign_header.yaml new file mode 100644 index 0000000000..8ebcdcb5bd --- /dev/null +++ b/gradle/changelog/redesign_header.yaml @@ -0,0 +1,2 @@ +- type: changed + description: Redesign SCM-Manager header ([#1721](https://github.com/scm-manager/scm-manager/pull/1721)) diff --git a/scm-ui/ui-components/src/Logo.stories.tsx b/scm-ui/ui-components/src/Logo.stories.tsx index ac8ab94c17..7502bbc8f2 100644 --- a/scm-ui/ui-components/src/Logo.stories.tsx +++ b/scm-ui/ui-components/src/Logo.stories.tsx @@ -32,8 +32,14 @@ const Wrapper = styled.div` height: 100%; `; -storiesOf("Logo", module).add("Default", () => ( - - - -)); +storiesOf("Logo", module) + .add("Default", () => ( + + + + )) + .add("WithoutText", () => ( + + + + )); diff --git a/scm-ui/ui-components/src/Logo.tsx b/scm-ui/ui-components/src/Logo.tsx index 7ead0b3724..b9ee5ea17f 100644 --- a/scm-ui/ui-components/src/Logo.tsx +++ b/scm-ui/ui-components/src/Logo.tsx @@ -21,15 +21,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -import React from "react"; -import { WithTranslation, withTranslation } from "react-i18next"; +import React, { FC } from "react"; +import { useTranslation } from "react-i18next"; import Image from "./Image"; -class Logo extends React.Component { - render() { - const { t } = this.props; - return {t("logo.alt")}; - } -} +type Props = { + withText?: boolean; + className?: string; +}; -export default withTranslation("commons")(Logo); +const Logo: FC = ({ withText = true, className }) => { + const [t] = useTranslation("commons"); + + if (withText) { + return {t("logo.alt")}; + } + return {t("logo.alt")}; +}; + +export default Logo; 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 74edafc842..a282bb69f0 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -53430,6 +53430,17 @@ exports[`Storyshots Logo Default 1`] = ` `; +exports[`Storyshots Logo WithoutText 1`] = ` +
+ logo.alt +
+`; + exports[`Storyshots MarkdownView Code without Lang 1`] = `
{ - render() { - const { children } = this.props; - return ( -
-
-
-
-
- -
+const SmallHeader: FC<{ children: ReactNode }> = ({ children }) => { + return
{children}
; +}; + +const LargeHeader: FC = () => { + return ( +
+
+
+
+
+
-
-
{children}
-
-
- ); +
+
+ ); +}; + +const Header: FC = ({ authenticated, children, links }) => { + if (authenticated) { + return {children}; + } else { + return ; } -} +}; export default Header; diff --git a/scm-ui/ui-components/src/navigation/PrimaryNavigation.tsx b/scm-ui/ui-components/src/navigation/PrimaryNavigation.tsx index b462e36048..7662ac1485 100644 --- a/scm-ui/ui-components/src/navigation/PrimaryNavigation.tsx +++ b/scm-ui/ui-components/src/navigation/PrimaryNavigation.tsx @@ -21,86 +21,59 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -import React, { ReactNode } from "react"; -import { WithTranslation, withTranslation } from "react-i18next"; +import React, { FC, ReactNode } from "react"; import PrimaryNavigationLink from "./PrimaryNavigationLink"; import { Links } from "@scm-manager/ui-types"; import { binder, ExtensionPoint } from "@scm-manager/ui-extensions"; import { urls } from "@scm-manager/ui-api"; -import { withRouter, RouteComponentProps } from "react-router-dom"; +import { useLocation } from "react-router-dom"; +import { useTranslation } from "react-i18next"; -type Props = RouteComponentProps & WithTranslation & { +type Props = { links: Links; }; type Appender = (to: string, match: string, label: string, linkName: string) => void; -class PrimaryNavigation extends React.Component { - createNavigationAppender = (navigationItems: ReactNode[]): Appender => { - const { t, links } = this.props; +const PrimaryNavigation: FC = ({ links }) => { + const [t] = useTranslation("commons"); + const location = useLocation(); + const createNavigationAppender = (navItems: ReactNode[]): Appender => { return (to: string, match: string, label: string, linkName: string) => { const link = links[linkName]; if (link) { - const navigationItem = ; - navigationItems.push(navigationItem); + const navigationItem = ( + + ); + navItems.push(navigationItem); } }; }; - appendLogout = (navigationItems: ReactNode[], append: Appender) => { - const { t, links } = this.props; + const createNavigationItems = () => { + const navItems: ReactNode[] = []; - const props = { + const extensionProps = { links, - label: t("primary-navigation.logout") + label: t("primary-navigation.first-menu"), }; - if (binder.hasExtension("primary-navigation.logout", props)) { - navigationItems.push( - - ); - } else { - append("/logout", "/logout", "primary-navigation.logout", "logout"); - } - }; - - appendLogin = (navigationItems: ReactNode[], append: Appender) => { - const { t, links, location } = this.props; - - const from = location.pathname; - const loginPath = "/login"; - const to = `${loginPath}?from=${encodeURIComponent(from)}`; - - const props = { - links, - label: t("primary-navigation.login"), - loginUrl: urls.withContextPath(loginPath), - from - }; - - if (binder.hasExtension("primary-navigation.login", props)) { - navigationItems.push( - - ); - } else { - append(to, "/login", "primary-navigation.login", "login"); - } - }; - - createNavigationItems = () => { - const navigationItems: ReactNode[] = []; - const { t, links } = this.props; - - const props = { - links, - label: t("primary-navigation.first-menu") - }; - - const append = this.createNavigationAppender(navigationItems); - if (binder.hasExtension("primary-navigation.first-menu", props)) { - navigationItems.push( - + const append = createNavigationAppender(navItems); + if (binder.hasExtension("primary-navigation.first-menu", extensionProps)) { + navItems.push( + ); } append("/repos/", "/(repo|repos)", "primary-navigation.repositories", "repositories"); @@ -108,32 +81,21 @@ class PrimaryNavigation extends React.Component { append("/groups/", "/(group|groups)", "primary-navigation.groups", "groups"); append("/admin", "/admin", "primary-navigation.admin", "config"); - navigationItems.push( + navItems.push( ); - this.appendLogout(navigationItems, append); - this.appendLogin(navigationItems, append); - - return navigationItems; + return navItems; }; - render() { - const navigationItems = this.createNavigationItems(); + return <>{createNavigationItems()}; +}; - return ( - - ); - } -} - -export default withTranslation("commons")(withRouter(PrimaryNavigation)); +export default PrimaryNavigation; diff --git a/scm-ui/ui-components/src/navigation/PrimaryNavigationLink.tsx b/scm-ui/ui-components/src/navigation/PrimaryNavigationLink.tsx index d06a4e422e..e5377215c8 100644 --- a/scm-ui/ui-components/src/navigation/PrimaryNavigationLink.tsx +++ b/scm-ui/ui-components/src/navigation/PrimaryNavigationLink.tsx @@ -21,42 +21,31 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -import * as React from "react"; -import { Route, Link } from "react-router-dom"; +import React, { FC } from "react"; +import { useRouteMatch, Link } from "react-router-dom"; import { createAttributesForTesting } from "../devBuild"; +import classNames from "classnames"; type Props = { to: string; + match: string; label: string; - match?: string; - activeOnlyWhenExact?: boolean; testId?: string; + className?: string; }; -class PrimaryNavigationLink extends React.Component { - renderLink = (route: any) => { - const { to, label, testId } = this.props; - return ( -
  • - - {label} - -
  • - ); - }; +const PrimaryNavigationLink: FC = ({ to, match, testId, label, className }) => { + const routeMatch = useRouteMatch({ path: match }); - render() { - const { to, match, activeOnlyWhenExact, testId } = this.props; - const path = match ? match : to; - return ( - - ); - } -} + return ( + + {label} + + ); +}; export default PrimaryNavigationLink; diff --git a/scm-ui/ui-styles/package.json b/scm-ui/ui-styles/package.json index c531a91bba..05ebd633e0 100644 --- a/scm-ui/ui-styles/package.json +++ b/scm-ui/ui-styles/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@fortawesome/fontawesome-free": "^5.11.2", - "bulma": "^0.9.0", + "bulma": "^0.9.3", "bulma-popover": "^1.0.0", "bulma-tooltip": "^3.0.0", "react-diff-view": "^2.4.1" diff --git a/scm-ui/ui-styles/src/scm.scss b/scm-ui/ui-styles/src/scm.scss index b5b1b25485..896106a8f1 100644 --- a/scm-ui/ui-styles/src/scm.scss +++ b/scm-ui/ui-styles/src/scm.scss @@ -60,10 +60,6 @@ $family-monospace: "Courier New", Monaco, Menlo, "Ubuntu Mono", "source-code-pro padding: 0 0 0 3.8em !important; } -.main { - min-height: calc(100vh - 300px); -} - // shown in top section when pageactions set hr.header-with-actions { margin-top: -10px; @@ -92,7 +88,6 @@ hr.header-with-actions { } footer.footer { - //height: 100px; background-color: $white-ter; padding: inherit; @@ -716,6 +711,13 @@ form .field:not(.is-grouped) { opacity: 1; } +.has-scm-background { + background-image: url(images/scmManagerHero.jpg) !important; + background-size: cover; + background-position: top center; + background-color: #002e4b; +} + // hero .hero.is-dark { background-color: #002e4b; diff --git a/scm-ui/ui-types/src/IndexResources.ts b/scm-ui/ui-types/src/IndexResources.ts index 1d9a44d73d..32a92dc843 100644 --- a/scm-ui/ui-types/src/IndexResources.ts +++ b/scm-ui/ui-types/src/IndexResources.ts @@ -22,10 +22,11 @@ * SOFTWARE. */ -import { Links } from "./hal"; +import { Embedded, Links } from "./hal"; export type IndexResources = { version: string; initialization?: string; _links: Links; + _embedded?: Embedded; }; diff --git a/scm-ui/ui-webapp/public/images/scmLogo.svg b/scm-ui/ui-webapp/public/images/scmLogo.svg new file mode 100644 index 0000000000..a911d82d95 --- /dev/null +++ b/scm-ui/ui-webapp/public/images/scmLogo.svg @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scm-ui/ui-webapp/src/containers/App.tsx b/scm-ui/ui-webapp/src/containers/App.tsx index 287ddf9e2a..f19bce55d1 100644 --- a/scm-ui/ui-webapp/src/containers/App.tsx +++ b/scm-ui/ui-webapp/src/containers/App.tsx @@ -24,11 +24,11 @@ import React, { FC } from "react"; import Main from "./Main"; import { useTranslation } from "react-i18next"; -import { ErrorPage, Footer, Header, Loading, PrimaryNavigation } from "@scm-manager/ui-components"; +import { ErrorPage, Footer, Header, Loading } from "@scm-manager/ui-components"; import { binder } from "@scm-manager/ui-extensions"; import Login from "./Login"; import { useIndex, useSubject } from "@scm-manager/ui-api"; -import Notifications from "./Notifications"; +import NavigationBar from "./NavigationBar"; const App: FC = () => { const { data: index } = useIndex(); @@ -46,7 +46,7 @@ const App: FC = () => { if (index?.initialization) { const Extension = binder.getExtension(`initialization.step.${index.initialization}`); - content = ; + content = ; } else if (!authenticated && !isLoading) { content = ; } else if (isLoading) { @@ -59,13 +59,8 @@ const App: FC = () => { return (
    -
    - {authenticated ? ( -
    - - -
    - ) : null} +
    +
    {content} {authenticated ?