feat: data menu routing (#10880)

This commit is contained in:
Moriah Kreeger 2020-09-15 14:53:31 -07:00 committed by GitHub
parent fe9f6148d8
commit c43992bec8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 148 additions and 12 deletions

View File

@ -0,0 +1,88 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { Link } from 'react-router-dom';
import { shallow } from 'enzyme';
import { Navbar, MenuItem } from 'react-bootstrap';
import SubMenu from 'src/components/Menu/SubMenu';
const defaultProps = {
name: 'Title',
children: [
{
name: 'Page1',
label: 'Page1',
url: '/page1',
usesRouter: true,
},
{
name: 'Page2',
label: 'Page2',
url: '/page2',
usesRouter: true,
},
{
name: 'Page3',
label: 'Page3',
url: '/page3',
usesRouter: false,
},
],
};
describe('SubMenu', () => {
let wrapper;
const getWrapper = (overrideProps = {}) => {
const props = {
...defaultProps,
...overrideProps,
};
return shallow(<SubMenu {...props} />);
};
beforeEach(() => {
wrapper = getWrapper();
});
it('renders a Navbar', () => {
expect(wrapper.find(Navbar)).toExist();
});
it('renders 3 MenuItems (when usesRouter === false)', () => {
expect(wrapper.find(MenuItem)).toHaveLength(3);
});
it('renders the menu title', () => {
expect(wrapper.find(Navbar.Brand)).toExist();
expect(wrapper.find(Navbar.Brand).children().text()).toEqual('Title');
});
it('renders Link components when usesRouter === true', () => {
const overrideProps = {
usesRouter: true,
};
const routerWrapper = getWrapper(overrideProps);
expect(routerWrapper.find(Link)).toExist();
expect(routerWrapper.find(Link)).toHaveLength(2);
expect(routerWrapper.find(MenuItem)).toHaveLength(1);
});
});

View File

@ -17,6 +17,7 @@
* under the License.
*/
import React from 'react';
import { Link, useHistory } from 'react-router-dom';
import { styled } from '@superset-ui/core';
import { Nav, Navbar, MenuItem } from 'react-bootstrap';
import Button, { OnClickHandler } from 'src/components/Button';
@ -31,16 +32,29 @@ const StyledHeader = styled.header`
}
.navbar-nav {
li {
a {
a,
div {
font-size: ${({ theme }) => theme.typography.sizes.s}px;
padding: ${({ theme }) => theme.gridUnit * 2}px;
padding: ${({ theme }) => theme.gridUnit * 2}px 0;
margin: ${({ theme }) => theme.gridUnit * 2}px;
color: ${({ theme }) => theme.colors.secondary.dark1};
a {
margin: 0;
padding: ${({ theme }) => theme.gridUnit * 4}px;
}
}
&.no-router a {
padding: ${({ theme }) => theme.gridUnit * 2}px
${({ theme }) => theme.gridUnit * 4}px;
}
}
li.active > a,
li > a:hover {
li.active > div,
li > a:hover,
li > div:hover {
background-color: ${({ theme }) => theme.colors.secondary.light4};
border-bottom: none;
border-radius: 4px;
@ -52,6 +66,7 @@ type MenuChild = {
label: string;
name: string;
url: string;
usesRouter?: boolean;
};
export interface SubMenuProps {
@ -66,9 +81,23 @@ export interface SubMenuProps {
name: string;
children?: MenuChild[];
activeChild?: MenuChild['name'];
/* If usesRouter is true, a react-router <Link> component will be used instead of href.
* ONLY set usesRouter to true if SubMenu is wrapped in a react-router <Router>;
* otherwise, a 'You should not use <Link> outside a <Router>' error will be thrown */
usesRouter?: boolean;
}
const SubMenu: React.FunctionComponent<SubMenuProps> = props => {
let hasHistory = true;
// If no parent <Router> component exists, useHistory throws an error
try {
useHistory();
} catch (err) {
// If error is thrown, we know not to use <Link> in render
hasHistory = false;
}
return (
<StyledHeader>
<Navbar inverse fluid role="navigation">
@ -77,15 +106,31 @@ const SubMenu: React.FunctionComponent<SubMenuProps> = props => {
</Navbar.Header>
<Nav>
{props.children &&
props.children.map(child => (
<MenuItem
active={child.name === props.activeChild}
key={`${child.label}`}
href={child.url}
>
{child.label}
</MenuItem>
))}
props.children.map(child => {
if ((props.usesRouter || hasHistory) && !!child.usesRouter) {
return (
<li
className={child.name === props.activeChild ? 'active' : ''}
key={`${child.label}`}
>
<div>
<Link to={child.url}>{child.label}</Link>
</div>
</li>
);
}
return (
<MenuItem
className="no-router"
active={child.name === props.activeChild}
key={`${child.label}`}
href={child.url}
>
{child.label}
</MenuItem>
);
})}
</Nav>
<Nav className="navbar-right">
{props.secondaryButton && (

View File

@ -25,16 +25,19 @@ export const commonMenuData = {
name: 'Datasets',
label: t('Datasets'),
url: '/tablemodelview/list/',
usesRouter: true,
},
{
name: 'Databases',
label: t('Databases'),
url: '/databaseview/list/',
usesRouter: true,
},
{
name: 'Saved Queries',
label: t('Saved Queries'),
url: '/sqllab/my_queries/',
usesRouter: false,
},
],
};