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 <[email protected]> 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;
