You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ly...@apache.org on 2023/02/03 18:34:54 UTC
[superset] 02/02: fix(frontend react key warnings): update key of SubMenu
This is an automated email from the ASF dual-hosted git repository.
lyndsi pushed a commit to branch fix-frontend-key-errors
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 8d5bb22f14c73c14d12c85ce858bbbf0b3133c4d
Author: michael_hoffman <mi...@nielsen.com>
AuthorDate: Sun Mar 6 08:45:33 2022 +0200
fix(frontend react key warnings): update key of SubMenu
---
superset-frontend/src/views/components/Menu.tsx | 2 +-
.../src/views/components/MenuRight.tsx | 361 +++++++++++++++++++++
2 files changed, 362 insertions(+), 1 deletion(-)
diff --git a/superset-frontend/src/views/components/Menu.tsx b/superset-frontend/src/views/components/Menu.tsx
index 09fbcd867f..4e6802eae0 100644
--- a/superset-frontend/src/views/components/Menu.tsx
+++ b/superset-frontend/src/views/components/Menu.tsx
@@ -213,7 +213,7 @@ export function Menu({
}
return (
<SubMenu
- key={`${index} - ${label}`}
+ key={label}
title={label}
icon={showMenu === 'inline' ? <></> : <Icons.TriangleDown />}
>
diff --git a/superset-frontend/src/views/components/MenuRight.tsx b/superset-frontend/src/views/components/MenuRight.tsx
new file mode 100644
index 0000000000..1ead9a314a
--- /dev/null
+++ b/superset-frontend/src/views/components/MenuRight.tsx
@@ -0,0 +1,361 @@
+/**
+ * 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, { useState } from 'react';
+import { MainNav as Menu } from 'src/components/Menu';
+import { t, styled, css, SupersetTheme } from '@superset-ui/core';
+import { Link } from 'react-router-dom';
+import Icons from 'src/components/Icons';
+import findPermission from 'src/dashboard/util/findPermission';
+import { useSelector } from 'react-redux';
+import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
+import LanguagePicker from './LanguagePicker';
+import DatabaseModal from '../CRUD/data/database/DatabaseModal';
+import { checkUploadExtensions } from '../CRUD/utils';
+import {
+ ExtentionConfigs,
+ GlobalMenuDataOptions,
+ RightMenuProps,
+} from './types';
+import { MenuObjectProps } from './Menu';
+
+const versionInfoStyles = (theme: SupersetTheme) => css`
+ padding: ${theme.gridUnit * 1.5}px ${theme.gridUnit * 4}px
+ ${theme.gridUnit * 4}px ${theme.gridUnit * 7}px;
+ color: ${theme.colors.grayscale.base};
+ font-size: ${theme.typography.sizes.xs}px;
+ white-space: nowrap;
+`;
+const StyledI = styled.div`
+ color: ${({ theme }) => theme.colors.primary.dark1};
+`;
+
+const StyledDiv = styled.div<{ align: string }>`
+ display: flex;
+ flex-direction: row;
+ justify-content: ${({ align }) => align};
+ align-items: center;
+ margin-right: ${({ theme }) => theme.gridUnit}px;
+ .ant-menu-submenu-title > svg {
+ top: ${({ theme }) => theme.gridUnit * 5.25}px;
+ }
+`;
+
+const StyledAnchor = styled.a`
+ padding-right: ${({ theme }) => theme.gridUnit}px;
+ padding-left: ${({ theme }) => theme.gridUnit}px;
+`;
+
+const { SubMenu } = Menu;
+
+const RightMenu = ({
+ align,
+ settings,
+ navbarRight,
+ isFrontendRoute,
+}: RightMenuProps) => {
+ const { roles } = useSelector<any, UserWithPermissionsAndRoles>(
+ state => state.user,
+ );
+ const {
+ CSV_EXTENSIONS,
+ COLUMNAR_EXTENSIONS,
+ EXCEL_EXTENSIONS,
+ ALLOWED_EXTENSIONS,
+ HAS_GSHEETS_INSTALLED,
+ } = useSelector<any, ExtentionConfigs>(state => state.common.conf);
+ const [showModal, setShowModal] = useState<boolean>(false);
+ const [engine, setEngine] = useState<string>('');
+ const canSql = findPermission('can_sqllab', 'Superset', roles);
+ const canDashboard = findPermission('can_write', 'Dashboard', roles);
+ const canChart = findPermission('can_write', 'Chart', roles);
+ const canDatabase = findPermission('can_write', 'Database', roles);
+
+ const canUploadCSV = findPermission(
+ 'can_this_form_get',
+ 'CsvToDatabaseView',
+ roles,
+ );
+ const canUploadColumnar = findPermission(
+ 'can_this_form_get',
+ 'ColumnarToDatabaseView',
+ roles,
+ );
+ const canUploadExcel = findPermission(
+ 'can_this_form_get',
+ 'ExcelToDatabaseView',
+ roles,
+ );
+
+ const canUpload = canUploadCSV || canUploadColumnar || canUploadExcel;
+ const showActionDropdown = canSql || canChart || canDashboard;
+ const dropdownItems: MenuObjectProps[] = [
+ {
+ label: t('Data'),
+ icon: 'fa-database',
+ childs: [
+ {
+ label: t('Connect database'),
+ name: GlobalMenuDataOptions.DB_CONNECTION,
+ perm: canDatabase,
+ },
+ {
+ label: t('Connect Google Sheet'),
+ name: GlobalMenuDataOptions.GOOGLE_SHEETS,
+ perm: canDatabase && HAS_GSHEETS_INSTALLED,
+ },
+ {
+ label: t('Upload CSV to database'),
+ name: 'Upload a CSV',
+ url: '/csvtodatabaseview/form',
+ perm:
+ checkUploadExtensions(CSV_EXTENSIONS, ALLOWED_EXTENSIONS) &&
+ canUploadCSV,
+ },
+ {
+ label: t('Upload columnar file to database'),
+ name: 'Upload a Columnar file',
+ url: '/columnartodatabaseview/form',
+ perm:
+ checkUploadExtensions(COLUMNAR_EXTENSIONS, ALLOWED_EXTENSIONS) &&
+ canUploadColumnar,
+ },
+ {
+ label: t('Upload Excel file to database'),
+ name: 'Upload Excel',
+ url: '/exceltodatabaseview/form',
+ perm:
+ checkUploadExtensions(EXCEL_EXTENSIONS, ALLOWED_EXTENSIONS) &&
+ canUploadExcel,
+ },
+ ],
+ },
+ {
+ label: t('SQL query'),
+ url: '/superset/sqllab?new=true',
+ icon: 'fa-fw fa-search',
+ perm: 'can_sqllab',
+ view: 'Superset',
+ },
+ {
+ label: t('Chart'),
+ url: '/chart/add',
+ icon: 'fa-fw fa-bar-chart',
+ perm: 'can_write',
+ view: 'Chart',
+ },
+ {
+ label: t('Dashboard'),
+ url: '/dashboard/new',
+ icon: 'fa-fw fa-dashboard',
+ perm: 'can_write',
+ view: 'Dashboard',
+ },
+ ];
+
+ const menuIconAndLabel = (menu: MenuObjectProps) => (
+ <>
+ <i data-test={`menu-item-${menu.label}`} className={`fa ${menu.icon}`} />
+ {menu.label}
+ </>
+ );
+
+ const handleMenuSelection = (itemChose: any) => {
+ if (itemChose.key === GlobalMenuDataOptions.DB_CONNECTION) {
+ setShowModal(true);
+ } else if (itemChose.key === GlobalMenuDataOptions.GOOGLE_SHEETS) {
+ setShowModal(true);
+ setEngine('Google Sheets');
+ }
+ };
+
+ const handleOnHideModal = () => {
+ setEngine('');
+ setShowModal(false);
+ };
+
+ return (
+ <StyledDiv align={align}>
+ <DatabaseModal
+ onHide={handleOnHideModal}
+ show={showModal}
+ dbEngine={engine}
+ />
+ <Menu selectable={false} mode="horizontal" onClick={handleMenuSelection}>
+ {!navbarRight.user_is_anonymous && showActionDropdown && (
+ <SubMenu
+ data-test="new-dropdown"
+ title={
+ <StyledI data-test="new-dropdown-icon" className="fa fa-plus" />
+ }
+ icon={<Icons.TriangleDown />}
+ >
+ {dropdownItems.map(menu => {
+ if (menu.childs) {
+ return canDatabase || canUpload ? (
+ <SubMenu
+ key="sub2"
+ className="data-menu"
+ title={menuIconAndLabel(menu)}
+ >
+ {menu.childs.map((item, idx) =>
+ typeof item !== 'string' && item.name && item.perm ? (
+ <div key={item.label}>
+ {idx === 2 && <Menu.Divider />}
+ <Menu.Item key={item.name}>
+ {item.url ? (
+ <a href={item.url}> {item.label} </a>
+ ) : (
+ item.label
+ )}
+ </Menu.Item>
+ </div>
+ ) : null,
+ )}
+ </SubMenu>
+ ) : null;
+ }
+ return (
+ findPermission(
+ menu.perm as string,
+ menu.view as string,
+ roles,
+ ) && (
+ <Menu.Item key={menu.label}>
+ <a href={menu.url}>
+ <i
+ data-test={`menu-item-${menu.label}`}
+ className={`fa ${menu.icon}`}
+ />{' '}
+ {menu.label}
+ </a>
+ </Menu.Item>
+ )
+ );
+ })}
+ </SubMenu>
+ )}
+ <SubMenu
+ title={t('Settings')}
+ icon={<Icons.TriangleDown iconSize="xl" />}
+ >
+ {settings.map((section, index) => [
+ <Menu.ItemGroup key={`${section.label}`} title={section.label}>
+ {section.childs?.map(child => {
+ if (typeof child !== 'string') {
+ return (
+ <Menu.Item key={`${child.label}`}>
+ {isFrontendRoute(child.url) ? (
+ <Link to={child.url || ''}>{child.label}</Link>
+ ) : (
+ <a href={child.url}>{child.label}</a>
+ )}
+ </Menu.Item>
+ );
+ }
+ return null;
+ })}
+ </Menu.ItemGroup>,
+ index < settings.length - 1 && <Menu.Divider />,
+ ])}
+
+ {!navbarRight.user_is_anonymous && [
+ <Menu.Divider key="user-divider" />,
+ <Menu.ItemGroup key="user-section" title={t('User')}>
+ {navbarRight.user_profile_url && (
+ <Menu.Item key="profile">
+ <a href={navbarRight.user_profile_url}>{t('Profile')}</a>
+ </Menu.Item>
+ )}
+ {navbarRight.user_info_url && (
+ <Menu.Item key="info">
+ <a href={navbarRight.user_info_url}>{t('Info')}</a>
+ </Menu.Item>
+ )}
+ <Menu.Item key="logout">
+ <a href={navbarRight.user_logout_url}>{t('Logout')}</a>
+ </Menu.Item>
+ </Menu.ItemGroup>,
+ ]}
+ {(navbarRight.version_string || navbarRight.version_sha) && [
+ <Menu.Divider key="version-info-divider" />,
+ <Menu.ItemGroup key="about-section" title={t('About')}>
+ <div className="about-section">
+ {navbarRight.show_watermark && (
+ <div css={versionInfoStyles}>
+ {t('Powered by Apache Superset')}
+ </div>
+ )}
+ {navbarRight.version_string && (
+ <div css={versionInfoStyles}>
+ Version: {navbarRight.version_string}
+ </div>
+ )}
+ {navbarRight.version_sha && (
+ <div css={versionInfoStyles}>
+ SHA: {navbarRight.version_sha}
+ </div>
+ )}
+ {navbarRight.build_number && (
+ <div css={versionInfoStyles}>
+ Build: {navbarRight.build_number}
+ </div>
+ )}
+ </div>
+ </Menu.ItemGroup>,
+ ]}
+ </SubMenu>
+ {navbarRight.show_language_picker && (
+ <LanguagePicker
+ locale={navbarRight.locale}
+ languages={navbarRight.languages}
+ />
+ )}
+ </Menu>
+ {navbarRight.documentation_url && (
+ <StyledAnchor
+ href={navbarRight.documentation_url}
+ target="_blank"
+ rel="noreferrer"
+ title={t('Documentation')}
+ >
+ <i className="fa fa-question" />
+
+ </StyledAnchor>
+ )}
+ {navbarRight.bug_report_url && (
+ <StyledAnchor
+ href={navbarRight.bug_report_url}
+ target="_blank"
+ rel="noreferrer"
+ title={t('Report a bug')}
+ >
+ <i className="fa fa-bug" />
+ </StyledAnchor>
+ )}
+ {navbarRight.user_is_anonymous && (
+ <StyledAnchor href={navbarRight.user_login_url}>
+ <i className="fa fa-fw fa-sign-in" />
+ {t('Login')}
+ </StyledAnchor>
+ )}
+ </StyledDiv>
+ );
+};
+
+export default RightMenu;