mirror of
https://github.com/getredash/redash.git
synced 2025-12-25 10:00:45 -05:00
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:
@@ -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>
|
||||
|
||||
@@ -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") && (
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user