Navbar: show correct settings link for non-admin users (#4978)

* Non-admin users have an access to some settings, so need to show correct menu item in navbar

* Refine settings test

* Add some tests
This commit is contained in:
Levko Kravets
2020-06-18 14:49:08 +03:00
committed by GitHub
parent 98a5154345
commit 676f560830
6 changed files with 71 additions and 35 deletions

View File

@@ -1,10 +1,12 @@
import { first } from "lodash";
import React, { useState } from "react";
import Button from "antd/lib/button";
import Menu from "antd/lib/menu";
import Icon from "antd/lib/icon";
import HelpTrigger from "@/components/HelpTrigger";
import { Auth, currentUser } from "@/services/auth";
import CreateDashboardDialog from "@/components/dashboards/CreateDashboardDialog";
import { Auth, currentUser } from "@/services/auth";
import settingsMenu from "@/services/settingsMenu";
import logoUrl from "@/assets/images/redash_icon_small.png";
import VersionInfo from "./VersionInfo";
@@ -26,6 +28,8 @@ function NavbarSection({ inlineCollapsed, children, ...props }) {
export default function DesktopNavbar() {
const [collapsed, setCollapsed] = useState(true);
const firstSettingsTab = first(settingsMenu.getAvailableItems());
return (
<div className="desktop-navbar">
<NavbarSection inlineCollapsed={collapsed} className="desktop-navbar-logo">
@@ -106,9 +110,9 @@ export default function DesktopNavbar() {
<span>Help</span>
</HelpTrigger>
</Menu.Item>
{currentUser.isAdmin && (
{firstSettingsTab && (
<Menu.Item key="settings">
<a href="data_sources">
<a href={firstSettingsTab.path} data-test="SettingsLink">
<Icon type="setting" />
<span>Settings</span>
</a>

View File

@@ -1,15 +1,19 @@
import { first } from "lodash";
import React from "react";
import PropTypes from "prop-types";
import logoUrl from "@/assets/images/redash_icon_small.png";
import Button from "antd/lib/button";
import Icon from "antd/lib/icon";
import Dropdown from "antd/lib/dropdown";
import Menu from "antd/lib/menu";
import { Auth, currentUser } from "@/services/auth";
import Button from "antd/lib/button";
import Icon from "antd/lib/icon";
import settingsMenu from "@/services/settingsMenu";
import logoUrl from "@/assets/images/redash_icon_small.png";
import "./MobileNavbar.less";
export default function MobileNavbar({ getPopupContainer }) {
const firstSettingsTab = first(settingsMenu.getAvailableItems());
return (
<div className="mobile-navbar">
<div className="mobile-navbar-logo">
@@ -43,9 +47,9 @@ export default function MobileNavbar({ getPopupContainer }) {
<a href="users/me">Edit Profile</a>
</Menu.Item>
<Menu.Divider />
{currentUser.isAdmin && (
{firstSettingsTab && (
<Menu.Item key="settings">
<a href="data_sources">Settings</a>
<a href={firstSettingsTab.path}>Settings</a>
</Menu.Item>
)}
{currentUser.hasPermission("super_admin") && (

View File

@@ -17,15 +17,13 @@ function wrapSettingsTab(options, WrappedComponent) {
<PageHeader title="Settings" />
<div className="bg-white tiled">
<Menu selectedKeys={[activeItem && activeItem.title]} selectable={false} mode="horizontal">
{settingsMenu.items
.filter(item => item.isAvailable())
.map(item => (
<Menu.Item key={item.title}>
<a href={item.path} data-test="SettingsScreenItem">
{item.title}
</a>
</Menu.Item>
))}
{settingsMenu.getAvailableItems().map(item => (
<Menu.Item key={item.title}>
<a href={item.path} data-test="SettingsScreenItem">
{item.title}
</a>
</Menu.Item>
))}
</Menu>
<div className="p-15">
<div>

View File

@@ -1,4 +1,4 @@
import { isFunction, extend, omit, sortBy, find } from "lodash";
import { isFunction, extend, omit, sortBy, find, filter } from "lodash";
import { stripBase } from "@/components/ApplicationArea/Router";
import { currentUser } from "@/services/auth";
@@ -29,6 +29,10 @@ class SettingsMenu {
this.items = sortBy(this.items, "order");
}
getAvailableItems() {
return filter(this.items, item => item.isAvailable());
}
getActiveItem(path) {
const strippedPath = stripBase(path);
return find(this.items, item => item.isActive(strippedPath));

View File

@@ -1,14 +1,11 @@
import { createUser } from "../../support/redash-api";
describe("Settings Tabs", () => {
before(() => {
cy.login();
createUser({
name: "Example User",
email: "user@redash.io",
password: "password",
});
});
const regularUser = {
name: "Example User",
email: "user@redash.io",
password: "password",
};
const userTabs = ["Users", "Groups", "Query Snippets", "Account"];
const adminTabs = ["Data Sources", "Alert Destinations", "Settings"];
@@ -19,16 +16,45 @@ describe("Settings Tabs", () => {
expect(listedPages).to.have.members(expectedTabs);
});
it("shows all tabs for admins", () => {
cy.visit("/users");
expectSettingsTabsToBe([...userTabs, ...adminTabs]);
before(() => {
cy.login().then(() => createUser(regularUser));
});
it("hides unavailable tabs for users", () => {
cy.logout()
.then(() => cy.login("user@redash.io", "password"))
.then(() => cy.visit("/users"));
describe("For admin user", () => {
beforeEach(() => {
cy.logout();
cy.login();
cy.visit("/");
});
expectSettingsTabsToBe(userTabs);
it("settings link should lead to Data Sources settings", () => {
cy.getByTestId("SettingsLink")
.should("exist")
.should("have.attr", "href", "data_sources");
});
it("all tabs should be available", () => {
cy.getByTestId("SettingsLink").click();
expectSettingsTabsToBe([...userTabs, ...adminTabs]);
});
});
describe("For regular user", () => {
beforeEach(() => {
cy.logout();
cy.login(regularUser.email, regularUser.password);
cy.visit("/");
});
it("settings link should lead to Users settings", () => {
cy.getByTestId("SettingsLink")
.should("exist")
.should("have.attr", "href", "users");
});
it("limited set of settings tabs should be available", () => {
cy.getByTestId("SettingsLink").click();
expectSettingsTabsToBe(userTabs);
});
});
});

View File

@@ -103,7 +103,7 @@ export function createUser({ name, email, password }) {
return cy
.request({
method: "POST",
url: "api/users",
url: "api/users?no_invite=yes",
body: { name, email },
failOnStatusCode: false,
})