From 91ea3b15fa0873b47d672b2701af6fc7245795e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 9 Jan 2020 11:25:52 +0100 Subject: [PATCH] Footer: Single footer component for both react & angular pages (#21389) * Footer: Single footer implementation for both react & angular pages * Export type * Updates * Use footer links in help menu * Updates & Fixes * Updated snapshot * updated snapshot --- pkg/api/index.go | 6 +- public/app/core/angular_wrappers.ts | 2 + .../app/core/components/Branding/Branding.tsx | 18 +++ public/app/core/components/Footer/Footer.tsx | 128 ++++++++++-------- .../app/core/components/Login/LoginPage.tsx | 5 +- public/app/core/components/Page/Page.tsx | 12 +- .../sidemenu/BottomNavLinks.test.tsx | 4 +- .../components/sidemenu/BottomNavLinks.tsx | 51 ++++--- .../BottomNavLinks.test.tsx.snap | 17 ++- public/app/features/admin/StyleGuideCtrl.ts | 29 ---- .../__snapshots__/ServerStats.test.tsx.snap | 25 ++-- public/app/features/admin/index.ts | 2 - .../features/admin/partials/admin_home.html | 1 + .../app/features/admin/partials/edit_org.html | 2 + .../features/admin/partials/edit_user.html | 2 + .../app/features/admin/partials/new_user.html | 2 + public/app/features/admin/partials/orgs.html | 2 + public/app/features/admin/partials/users.html | 2 + .../alerting/partials/notification_edit.html | 2 + .../alerting/partials/notifications_list.html | 2 + .../folders/partials/create_folder.html | 2 + .../folders/partials/folder_dashboards.html | 4 +- .../partials/dashboard_import.html | 2 + .../partials/dashboard_list.html | 2 + .../partials/snapshot_list.html | 2 + public/app/features/org/partials/newOrg.html | 2 + .../app/features/org/partials/select_org.html | 1 + .../features/playlist/partials/playlist.html | 2 + .../features/playlist/partials/playlists.html | 6 +- .../plugins/partials/plugin_page.html | 2 + .../features/profile/partials/profile.html | 2 + public/app/partials/error.html | 2 + public/app/partials/reset_password.html | 2 + public/app/partials/signup_invited.html | 2 +- public/app/partials/signup_step2.html | 4 +- public/app/routes/routes.ts | 5 - public/sass/_grafana.scss | 1 - public/sass/components/_footer.scss | 18 +-- public/sass/pages/_styleguide.scss | 33 ----- public/views/index-template.html | 40 +----- 40 files changed, 209 insertions(+), 239 deletions(-) create mode 100644 public/app/core/components/Branding/Branding.tsx delete mode 100644 public/app/features/admin/StyleGuideCtrl.ts delete mode 100644 public/sass/pages/_styleguide.scss diff --git a/pkg/api/index.go b/pkg/api/index.go index 916e606ed18..dc192558145 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -352,11 +352,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er Icon: "gicon gicon-question", HideFromMenu: true, SortWeight: dtos.WeightHelp, - Children: []*dtos.NavLink{ - {Text: "Keyboard shortcuts", Url: "/shortcuts", Icon: "fa fa-fw fa-keyboard-o", Target: "_self"}, - {Text: "Community site", Url: "http://community.grafana.com", Icon: "fa fa-fw fa-comment", Target: "_blank"}, - {Text: "Documentation", Url: "http://docs.grafana.org", Icon: "fa fa-fw fa-file", Target: "_blank"}, - }, + Children: []*dtos.NavLink{}, }) hs.HooksService.RunIndexDataHooks(&data) diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts index 0f2c25e39d3..8cf3ef5b48b 100644 --- a/public/app/core/angular_wrappers.ts +++ b/public/app/core/angular_wrappers.ts @@ -21,8 +21,10 @@ import { GraphContextMenu } from 'app/plugins/panel/graph/GraphContextMenu'; import ReactProfileWrapper from 'app/features/profile/ReactProfileWrapper'; import { LokiAnnotationsQueryEditor } from '../plugins/datasource/loki/components/AnnotationsQueryEditor'; import { HelpModal } from './components/help/HelpModal'; +import { Footer } from './components/Footer/Footer'; export function registerAngularDirectives() { + react2AngularDirective('footer', Footer, []); react2AngularDirective('helpModal', HelpModal, []); react2AngularDirective('sidemenu', SideMenu, []); react2AngularDirective('functionEditor', FunctionEditor, ['func', 'onRemove', 'onMoveLeft', 'onMoveRight']); diff --git a/public/app/core/components/Branding/Branding.tsx b/public/app/core/components/Branding/Branding.tsx new file mode 100644 index 00000000000..b1e193bdf21 --- /dev/null +++ b/public/app/core/components/Branding/Branding.tsx @@ -0,0 +1,18 @@ +import React, { FC } from 'react'; + +export interface BrandComponentProps { + className: string; +} + +export const LogoIcon: FC = ({ className }) => { + return Grafana; +}; + +export const Wordmark: FC = ({ className }) => { + return
; +}; + +export class Branding { + static LogoIcon = LogoIcon; + static Wordmark = Wordmark; +} diff --git a/public/app/core/components/Footer/Footer.tsx b/public/app/core/components/Footer/Footer.tsx index 07d31f0fe58..ab4a6ed111f 100644 --- a/public/app/core/components/Footer/Footer.tsx +++ b/public/app/core/components/Footer/Footer.tsx @@ -1,61 +1,77 @@ import React, { FC } from 'react'; -import { Tooltip } from '@grafana/ui'; +import config from 'app/core/config'; -interface Props { - appName: string; - buildVersion: string; - buildCommit: string; - newGrafanaVersionExists: boolean; - newGrafanaVersion: string; +export interface FooterLink { + text: string; + icon?: string; + url?: string; } -export const Footer: FC = React.memo( - ({ appName, buildVersion, buildCommit, newGrafanaVersionExists, newGrafanaVersion }) => { - return ( - - ); - } -); +export let getFooterLinks = (): FooterLink[] => { + return [ + { + text: 'Docs', + icon: 'fa fa-file-code-o', + url: 'https://grafana.com/docs/grafana/latest/?utm_source=grafana_footer', + }, + { + text: 'Support & Enterprise', + icon: 'fa fa-support', + url: 'https://grafana.com/products/enterprise/?utm_source=grafana_footer', + }, + { + text: 'Community', + icon: 'fa fa-comments-o', + url: 'https://community.grafana.com/?utm_source=grafana_footer', + }, + ]; +}; -export default Footer; +export let getVersionLinks = (): FooterLink[] => { + const { buildInfo } = config; + + const links: FooterLink[] = [ + { + text: `Grafana v${buildInfo.version} (commit: ${buildInfo.commit})`, + url: 'https://grafana.com', + }, + ]; + + if (buildInfo.hasUpdate) { + links.push({ + text: `New version available!`, + icon: 'fa fa-download', + url: 'https://grafana.com/grafana/download?utm_source=grafana_footer', + }); + } + + return links; +}; + +export function setFooterLinksFn(fn: typeof getFooterLinks) { + getFooterLinks = fn; +} + +export function setVersionLinkFn(fn: typeof getFooterLinks) { + getVersionLinks = fn; +} + +export const Footer: FC = React.memo(() => { + const links = getFooterLinks().concat(getVersionLinks()); + + return ( + + ); +}); diff --git a/public/app/core/components/Login/LoginPage.tsx b/public/app/core/components/Login/LoginPage.tsx index 610dace71b8..98ef1e4c9f2 100644 --- a/public/app/core/components/Login/LoginPage.tsx +++ b/public/app/core/components/Login/LoginPage.tsx @@ -5,14 +5,15 @@ import LoginCtrl from './LoginCtrl'; import { LoginForm } from './LoginForm'; import { ChangePassword } from './ChangePassword'; import { CSSTransition } from 'react-transition-group'; +import { Branding } from 'app/core/components/Branding/Branding'; export const LoginPage: FC = () => { return (
- Grafana -
+ +
{({ diff --git a/public/app/core/components/Page/Page.tsx b/public/app/core/components/Page/Page.tsx index a2a8bc1d621..e906aa25dca 100644 --- a/public/app/core/components/Page/Page.tsx +++ b/public/app/core/components/Page/Page.tsx @@ -1,11 +1,10 @@ // Libraries import React, { Component } from 'react'; -import config from 'app/core/config'; import { getTitleFromNavModel } from 'app/core/selectors/navModel'; // Components import PageHeader from '../PageHeader/PageHeader'; -import Footer from '../Footer/Footer'; +import { Footer } from '../Footer/Footer'; import PageContents from './PageContents'; import { CustomScrollbar } from '@grafana/ui'; import { NavModel } from '@grafana/data'; @@ -45,20 +44,13 @@ class Page extends Component { render() { const { navModel } = this.props; - const { buildInfo } = config; return (
{this.props.children} -
+
diff --git a/public/app/core/components/sidemenu/BottomNavLinks.test.tsx b/public/app/core/components/sidemenu/BottomNavLinks.test.tsx index 804cffc7f80..94726184eb1 100644 --- a/public/app/core/components/sidemenu/BottomNavLinks.test.tsx +++ b/public/app/core/components/sidemenu/BottomNavLinks.test.tsx @@ -90,11 +90,9 @@ describe('Render', () => { describe('Functions', () => { describe('item clicked', () => { const wrapper = setup(); - const mockEvent = { preventDefault: jest.fn() }; it('should emit show modal event if url matches shortcut', () => { - const child = { url: '/shortcuts', text: 'hello' }; const instance = wrapper.instance() as BottomNavLinks; - instance.itemClicked(mockEvent as any, child); + instance.onOpenShortcuts(); expect(appEvents.emit).toHaveBeenCalledWith(CoreEvents.showModal, { templateHtml: '' }); }); diff --git a/public/app/core/components/sidemenu/BottomNavLinks.tsx b/public/app/core/components/sidemenu/BottomNavLinks.tsx index 3210a642e28..d1e1cc5811e 100644 --- a/public/app/core/components/sidemenu/BottomNavLinks.tsx +++ b/public/app/core/components/sidemenu/BottomNavLinks.tsx @@ -4,6 +4,7 @@ import { User } from '../../services/context_srv'; import { NavModelItem } from '@grafana/data'; import { CoreEvents } from 'app/types'; import { OrgSwitcher } from '../OrgSwitcher'; +import { getFooterLinks } from '../Footer/Footer'; export interface Props { link: NavModelItem; @@ -19,13 +20,10 @@ class BottomNavLinks extends PureComponent { showSwitcherModal: false, }; - itemClicked = (event: React.SyntheticEvent, child: NavModelItem) => { - if (child.url === '/shortcuts') { - event.preventDefault(); - appEvents.emit(CoreEvents.showModal, { - templateHtml: '', - }); - } + onOpenShortcuts = () => { + appEvents.emit(CoreEvents.showModal, { + templateHtml: '', + }); }; toggleSwitcherModal = () => { @@ -38,6 +36,12 @@ class BottomNavLinks extends PureComponent { const { link, user } = this.props; const { showSwitcherModal } = this.state; + let children = link.children || []; + + if (link.id === 'help') { + children = getFooterLinks(); + } + return (
@@ -69,20 +73,25 @@ class BottomNavLinks extends PureComponent { {showSwitcherModal && } - {link.children && - link.children.map((child, index) => { - if (!child.hideFromMenu) { - return ( -
  • - this.itemClicked(event, child)}> - {child.icon && } - {child.text} - -
  • - ); - } - return null; - })} + {children.map((child, index) => { + return ( +
  • + + {child.icon && } + {child.text} + +
  • + ); + })} + + {link.id === 'help' && ( +
  • + this.onOpenShortcuts()}> + Keyboard shortcuts + +
  • + )} +
  • {link.text}
  • diff --git a/public/app/core/components/sidemenu/__snapshots__/BottomNavLinks.test.tsx.snap b/public/app/core/components/sidemenu/__snapshots__/BottomNavLinks.test.tsx.snap index be720cf3023..4474b36ee22 100644 --- a/public/app/core/components/sidemenu/__snapshots__/BottomNavLinks.test.tsx.snap +++ b/public/app/core/components/sidemenu/__snapshots__/BottomNavLinks.test.tsx.snap @@ -19,21 +19,32 @@ exports[`Render should render children 1`] = ` key="undefined-0" >
  • +
  • +
  • +
  • { - window.location.href = window.location.href; - }); - } -} diff --git a/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap b/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap index b0ee7f5686f..af6321ddd7d 100644 --- a/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap +++ b/public/app/features/admin/__snapshots__/ServerStats.test.tsx.snap @@ -159,14 +159,15 @@ exports[`ServerStats Should render table with stats 1`] = `
    • - Docs + + Docs
    • @@ -178,19 +179,21 @@ exports[`ServerStats Should render table with stats 1`] = ` - Support & Enterprise + + Support & Enterprise
    • - Community + + Community
    • @@ -199,16 +202,10 @@ exports[`ServerStats Should render table with stats 1`] = ` rel="noopener" target="_blank" > - Grafana + + + Grafana vv1.0 (commit: 1) - - - v - v1.0 - (commit: - 1 - ) -
  • diff --git a/public/app/features/admin/index.ts b/public/app/features/admin/index.ts index 396653bfc24..b9b60229b50 100644 --- a/public/app/features/admin/index.ts +++ b/public/app/features/admin/index.ts @@ -2,7 +2,6 @@ import AdminListUsersCtrl from './AdminListUsersCtrl'; import AdminEditUserCtrl from './AdminEditUserCtrl'; import AdminListOrgsCtrl from './AdminListOrgsCtrl'; import AdminEditOrgCtrl from './AdminEditOrgCtrl'; -import StyleGuideCtrl from './StyleGuideCtrl'; import coreModule from 'app/core/core_module'; import { NavModelSrv } from 'app/core/core'; @@ -21,4 +20,3 @@ coreModule.controller('AdminEditUserCtrl', AdminEditUserCtrl); coreModule.controller('AdminListOrgsCtrl', AdminListOrgsCtrl); coreModule.controller('AdminEditOrgCtrl', AdminEditOrgCtrl); coreModule.controller('AdminHomeCtrl', AdminHomeCtrl); -coreModule.controller('StyleGuideCtrl', StyleGuideCtrl); diff --git a/public/app/features/admin/partials/admin_home.html b/public/app/features/admin/partials/admin_home.html index ea61385f006..a3432e61dc0 100644 --- a/public/app/features/admin/partials/admin_home.html +++ b/public/app/features/admin/partials/admin_home.html @@ -9,3 +9,4 @@
    +
    diff --git a/public/app/features/admin/partials/edit_org.html b/public/app/features/admin/partials/edit_org.html index 49455720da4..356fbaf14ab 100644 --- a/public/app/features/admin/partials/edit_org.html +++ b/public/app/features/admin/partials/edit_org.html @@ -42,3 +42,5 @@
    + +
    diff --git a/public/app/features/admin/partials/edit_user.html b/public/app/features/admin/partials/edit_user.html index 7182f123149..a60ae98de6d 100644 --- a/public/app/features/admin/partials/edit_user.html +++ b/public/app/features/admin/partials/edit_user.html @@ -181,3 +181,5 @@
    + +