mirror of
https://github.com/apache/superset.git
synced 2024-09-16 02:29:39 -04:00
feat: allow uploads in crud view (#18953)
* feat: allow uploads in crud view * fix merge conflict and fix ts * fix import * fix tests * fix lint * remove unused var * fix underline flash and alignment * fix offset * fix icon alignment * fix labels and css issues * make drowdown primary all the time * make global * fix lables * add upload perms to utils * remove unused code * add suggested changes * update menuright
This commit is contained in:
parent
9ae51f7a48
commit
d771ddbb94
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
|
import * as redux from 'react-redux';
|
||||||
import configureStore from 'redux-mock-store';
|
import configureStore from 'redux-mock-store';
|
||||||
import fetchMock from 'fetch-mock';
|
import fetchMock from 'fetch-mock';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
@ -40,6 +41,17 @@ import { act } from 'react-dom/test-utils';
|
|||||||
const mockStore = configureStore([thunk]);
|
const mockStore = configureStore([thunk]);
|
||||||
const store = mockStore({});
|
const store = mockStore({});
|
||||||
|
|
||||||
|
const mockAppState = {
|
||||||
|
common: {
|
||||||
|
config: {
|
||||||
|
CSV_EXTENSIONS: ['csv'],
|
||||||
|
EXCEL_EXTENSIONS: ['xls', 'xlsx'],
|
||||||
|
COLUMNAR_EXTENSIONS: ['parquet', 'zip'],
|
||||||
|
ALLOWED_EXTENSIONS: ['parquet', 'zip', 'xls', 'xlsx', 'csv'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const databasesInfoEndpoint = 'glob:*/api/v1/database/_info*';
|
const databasesInfoEndpoint = 'glob:*/api/v1/database/_info*';
|
||||||
const databasesEndpoint = 'glob:*/api/v1/database/?*';
|
const databasesEndpoint = 'glob:*/api/v1/database/?*';
|
||||||
const databaseEndpoint = 'glob:*/api/v1/database/*';
|
const databaseEndpoint = 'glob:*/api/v1/database/*';
|
||||||
@ -94,12 +106,22 @@ fetchMock.get(databaseRelatedEndpoint, {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const useSelectorMock = jest.spyOn(redux, 'useSelector');
|
||||||
|
|
||||||
describe('DatabaseList', () => {
|
describe('DatabaseList', () => {
|
||||||
|
useSelectorMock.mockReturnValue({
|
||||||
|
CSV_EXTENSIONS: ['csv'],
|
||||||
|
EXCEL_EXTENSIONS: ['xls', 'xlsx'],
|
||||||
|
COLUMNAR_EXTENSIONS: ['parquet', 'zip'],
|
||||||
|
ALLOWED_EXTENSIONS: ['parquet', 'zip', 'xls', 'xlsx', 'csv'],
|
||||||
|
});
|
||||||
|
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<DatabaseList user={mockUser} />
|
<DatabaseList user={mockUser} />
|
||||||
</Provider>,
|
</Provider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await waitForComponentToPaint(wrapper);
|
await waitForComponentToPaint(wrapper);
|
||||||
});
|
});
|
||||||
@ -195,6 +217,7 @@ describe('RTL', () => {
|
|||||||
<DatabaseList user={mockUser} />
|
<DatabaseList user={mockUser} />
|
||||||
</QueryParamProvider>,
|
</QueryParamProvider>,
|
||||||
{ useRedux: true },
|
{ useRedux: true },
|
||||||
|
mockAppState,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18,10 +18,11 @@
|
|||||||
*/
|
*/
|
||||||
import { SupersetClient, t, styled } from '@superset-ui/core';
|
import { SupersetClient, t, styled } from '@superset-ui/core';
|
||||||
import React, { useState, useMemo } from 'react';
|
import React, { useState, useMemo } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import Loading from 'src/components/Loading';
|
import Loading from 'src/components/Loading';
|
||||||
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
|
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
|
||||||
import { useListViewResource } from 'src/views/CRUD/hooks';
|
import { useListViewResource } from 'src/views/CRUD/hooks';
|
||||||
import { createErrorHandler } from 'src/views/CRUD/utils';
|
import { createErrorHandler, uploadUserPerms } from 'src/views/CRUD/utils';
|
||||||
import withToasts from 'src/components/MessageToasts/withToasts';
|
import withToasts from 'src/components/MessageToasts/withToasts';
|
||||||
import SubMenu, { SubMenuProps } from 'src/views/components/SubMenu';
|
import SubMenu, { SubMenuProps } from 'src/views/components/SubMenu';
|
||||||
import DeleteModal from 'src/components/DeleteModal';
|
import DeleteModal from 'src/components/DeleteModal';
|
||||||
@ -31,6 +32,8 @@ import ListView, { FilterOperator, Filters } from 'src/components/ListView';
|
|||||||
import { commonMenuData } from 'src/views/CRUD/data/common';
|
import { commonMenuData } from 'src/views/CRUD/data/common';
|
||||||
import ImportModelsModal from 'src/components/ImportModal/index';
|
import ImportModelsModal from 'src/components/ImportModal/index';
|
||||||
import handleResourceExport from 'src/utils/export';
|
import handleResourceExport from 'src/utils/export';
|
||||||
|
import { ExtentionConfigs } from 'src/views/components/types';
|
||||||
|
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
||||||
import DatabaseModal from './DatabaseModal';
|
import DatabaseModal from './DatabaseModal';
|
||||||
|
|
||||||
import { DatabaseObject } from './types';
|
import { DatabaseObject } from './types';
|
||||||
@ -103,6 +106,15 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) {
|
|||||||
const [importingDatabase, showImportModal] = useState<boolean>(false);
|
const [importingDatabase, showImportModal] = useState<boolean>(false);
|
||||||
const [passwordFields, setPasswordFields] = useState<string[]>([]);
|
const [passwordFields, setPasswordFields] = useState<string[]>([]);
|
||||||
const [preparingExport, setPreparingExport] = useState<boolean>(false);
|
const [preparingExport, setPreparingExport] = useState<boolean>(false);
|
||||||
|
const { roles } = useSelector<any, UserWithPermissionsAndRoles>(
|
||||||
|
state => state.user,
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
CSV_EXTENSIONS,
|
||||||
|
COLUMNAR_EXTENSIONS,
|
||||||
|
EXCEL_EXTENSIONS,
|
||||||
|
ALLOWED_EXTENSIONS,
|
||||||
|
} = useSelector<any, ExtentionConfigs>(state => state.common.conf);
|
||||||
|
|
||||||
const openDatabaseImportModal = () => {
|
const openDatabaseImportModal = () => {
|
||||||
showImportModal(true);
|
showImportModal(true);
|
||||||
@ -171,8 +183,49 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) {
|
|||||||
const canExport =
|
const canExport =
|
||||||
hasPerm('can_export') && isFeatureEnabled(FeatureFlag.VERSIONED_EXPORT);
|
hasPerm('can_export') && isFeatureEnabled(FeatureFlag.VERSIONED_EXPORT);
|
||||||
|
|
||||||
|
const { canUploadCSV, canUploadColumnar, canUploadExcel } = uploadUserPerms(
|
||||||
|
roles,
|
||||||
|
CSV_EXTENSIONS,
|
||||||
|
COLUMNAR_EXTENSIONS,
|
||||||
|
EXCEL_EXTENSIONS,
|
||||||
|
ALLOWED_EXTENSIONS,
|
||||||
|
);
|
||||||
|
|
||||||
|
const uploadDropdownMenu = [
|
||||||
|
{
|
||||||
|
label: t('Upload file to database'),
|
||||||
|
childs: [
|
||||||
|
{
|
||||||
|
label: t('Upload CSV'),
|
||||||
|
name: 'Upload CSV file',
|
||||||
|
url: '/csvtodatabaseview/form',
|
||||||
|
perm: canUploadCSV,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('Upload columnar file'),
|
||||||
|
name: 'Upload columnar file',
|
||||||
|
url: '/columnartodatabaseview/form',
|
||||||
|
perm: canUploadColumnar,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('Upload Excel file'),
|
||||||
|
name: 'Upload Excel file',
|
||||||
|
url: '/exceltodatabaseview/form',
|
||||||
|
perm: canUploadExcel,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const filteredDropDown = uploadDropdownMenu.map(link => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
link.childs = link.childs.filter(item => item.perm);
|
||||||
|
return link;
|
||||||
|
});
|
||||||
|
|
||||||
const menuData: SubMenuProps = {
|
const menuData: SubMenuProps = {
|
||||||
activeChild: 'Databases',
|
activeChild: 'Databases',
|
||||||
|
dropDownLinks: filteredDropDown,
|
||||||
...commonMenuData,
|
...commonMenuData,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -222,6 +275,7 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
|
const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
|
@ -32,6 +32,7 @@ import rison from 'rison';
|
|||||||
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
|
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
|
||||||
import { FetchDataConfig } from 'src/components/ListView';
|
import { FetchDataConfig } from 'src/components/ListView';
|
||||||
import SupersetText from 'src/utils/textUtils';
|
import SupersetText from 'src/utils/textUtils';
|
||||||
|
import findPermission from 'src/dashboard/util/findPermission';
|
||||||
import { Dashboard, Filters } from './types';
|
import { Dashboard, Filters } from './types';
|
||||||
|
|
||||||
// Modifies the rison encoding slightly to match the backend's rison encoding/decoding. Applies globally.
|
// Modifies the rison encoding slightly to match the backend's rison encoding/decoding. Applies globally.
|
||||||
@ -420,3 +421,21 @@ export const checkUploadExtensions = (
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const uploadUserPerms = (
|
||||||
|
roles: Record<string, [string, string][]>,
|
||||||
|
csvExt: Array<string>,
|
||||||
|
colExt: Array<string>,
|
||||||
|
excelExt: Array<string>,
|
||||||
|
allowedExt: Array<string>,
|
||||||
|
) => ({
|
||||||
|
canUploadCSV:
|
||||||
|
findPermission('can_this_form_get', 'CsvToDatabaseView', roles) &&
|
||||||
|
checkUploadExtensions(csvExt, allowedExt),
|
||||||
|
canUploadColumnar:
|
||||||
|
checkUploadExtensions(colExt, allowedExt) &&
|
||||||
|
findPermission('can_this_form_get', 'ColumnarToDatabaseView', roles),
|
||||||
|
canUploadExcel:
|
||||||
|
checkUploadExtensions(excelExt, allowedExt) &&
|
||||||
|
findPermission('can_this_form_get', 'ExcelToDatabaseView', roles),
|
||||||
|
});
|
||||||
|
@ -66,7 +66,7 @@ export interface MenuProps {
|
|||||||
isFrontendRoute?: (path?: string) => boolean;
|
isFrontendRoute?: (path?: string) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MenuObjectChildProps {
|
export interface MenuObjectChildProps {
|
||||||
label: string;
|
label: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
@ -26,7 +26,7 @@ import { useSelector } from 'react-redux';
|
|||||||
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
||||||
import LanguagePicker from './LanguagePicker';
|
import LanguagePicker from './LanguagePicker';
|
||||||
import DatabaseModal from '../CRUD/data/database/DatabaseModal';
|
import DatabaseModal from '../CRUD/data/database/DatabaseModal';
|
||||||
import { checkUploadExtensions } from '../CRUD/utils';
|
import { uploadUserPerms } from '../CRUD/utils';
|
||||||
import {
|
import {
|
||||||
ExtentionConfigs,
|
ExtentionConfigs,
|
||||||
GlobalMenuDataOptions,
|
GlobalMenuDataOptions,
|
||||||
@ -86,20 +86,12 @@ const RightMenu = ({
|
|||||||
const canChart = findPermission('can_write', 'Chart', roles);
|
const canChart = findPermission('can_write', 'Chart', roles);
|
||||||
const canDatabase = findPermission('can_write', 'Database', roles);
|
const canDatabase = findPermission('can_write', 'Database', roles);
|
||||||
|
|
||||||
const canUploadCSV = findPermission(
|
const { canUploadCSV, canUploadColumnar, canUploadExcel } = uploadUserPerms(
|
||||||
'can_this_form_get',
|
|
||||||
'CsvToDatabaseView',
|
|
||||||
roles,
|
|
||||||
);
|
|
||||||
const canUploadColumnar = findPermission(
|
|
||||||
'can_this_form_get',
|
|
||||||
'ColumnarToDatabaseView',
|
|
||||||
roles,
|
|
||||||
);
|
|
||||||
const canUploadExcel = findPermission(
|
|
||||||
'can_this_form_get',
|
|
||||||
'ExcelToDatabaseView',
|
|
||||||
roles,
|
roles,
|
||||||
|
CSV_EXTENSIONS,
|
||||||
|
COLUMNAR_EXTENSIONS,
|
||||||
|
EXCEL_EXTENSIONS,
|
||||||
|
ALLOWED_EXTENSIONS,
|
||||||
);
|
);
|
||||||
|
|
||||||
const canUpload = canUploadCSV || canUploadColumnar || canUploadExcel;
|
const canUpload = canUploadCSV || canUploadColumnar || canUploadExcel;
|
||||||
@ -123,25 +115,19 @@ const RightMenu = ({
|
|||||||
label: t('Upload CSV to database'),
|
label: t('Upload CSV to database'),
|
||||||
name: 'Upload a CSV',
|
name: 'Upload a CSV',
|
||||||
url: '/csvtodatabaseview/form',
|
url: '/csvtodatabaseview/form',
|
||||||
perm:
|
perm: canUploadCSV,
|
||||||
checkUploadExtensions(CSV_EXTENSIONS, ALLOWED_EXTENSIONS) &&
|
|
||||||
canUploadCSV,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('Upload columnar file to database'),
|
label: t('Upload columnar file to database'),
|
||||||
name: 'Upload a Columnar file',
|
name: 'Upload a Columnar file',
|
||||||
url: '/columnartodatabaseview/form',
|
url: '/columnartodatabaseview/form',
|
||||||
perm:
|
perm: canUploadColumnar,
|
||||||
checkUploadExtensions(COLUMNAR_EXTENSIONS, ALLOWED_EXTENSIONS) &&
|
|
||||||
canUploadColumnar,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('Upload Excel file to database'),
|
label: t('Upload Excel file to database'),
|
||||||
name: 'Upload Excel',
|
name: 'Upload Excel',
|
||||||
url: '/exceltodatabaseview/form',
|
url: '/exceltodatabaseview/form',
|
||||||
perm:
|
perm: canUploadExcel,
|
||||||
checkUploadExtensions(EXCEL_EXTENSIONS, ALLOWED_EXTENSIONS) &&
|
|
||||||
canUploadExcel,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -43,6 +43,19 @@ const mockedProps = {
|
|||||||
usesRouter: false,
|
usesRouter: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
dropDownLinks: [
|
||||||
|
{
|
||||||
|
label: 'test a upload',
|
||||||
|
childs: [
|
||||||
|
{
|
||||||
|
label: 'Upload Test',
|
||||||
|
name: 'Upload Test',
|
||||||
|
url: '/test/form',
|
||||||
|
perm: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
test('should render', () => {
|
test('should render', () => {
|
||||||
@ -74,6 +87,13 @@ test('should render all the tabs links', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should render dropdownlinks', async () => {
|
||||||
|
render(<SubMenu {...mockedProps} />);
|
||||||
|
userEvent.hover(screen.getByText('test a upload'));
|
||||||
|
const label = await screen.findByText('test a upload');
|
||||||
|
expect(label).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
test('should render the buttons', () => {
|
test('should render the buttons', () => {
|
||||||
const mockFunc = jest.fn();
|
const mockFunc = jest.fn();
|
||||||
const buttons = [
|
const buttons = [
|
||||||
@ -94,7 +114,7 @@ test('should render the buttons', () => {
|
|||||||
};
|
};
|
||||||
render(<SubMenu {...buttonsProps} />);
|
render(<SubMenu {...buttonsProps} />);
|
||||||
const testButton = screen.getByText(buttons[0].name);
|
const testButton = screen.getByText(buttons[0].name);
|
||||||
expect(screen.getAllByRole('button')).toHaveLength(2);
|
expect(screen.getAllByRole('button')).toHaveLength(3);
|
||||||
userEvent.click(testButton);
|
userEvent.click(testButton);
|
||||||
expect(mockFunc).toHaveBeenCalled();
|
expect(mockFunc).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -22,8 +22,10 @@ import { styled } from '@superset-ui/core';
|
|||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { Row } from 'src/components';
|
import { Row } from 'src/components';
|
||||||
import { Menu, MenuMode } from 'src/components/Menu';
|
import { Menu, MenuMode, MainNav as DropdownMenu } from 'src/components/Menu';
|
||||||
import Button, { OnClickHandler } from 'src/components/Button';
|
import Button, { OnClickHandler } from 'src/components/Button';
|
||||||
|
import Icons from 'src/components/Icons';
|
||||||
|
import { MenuObjectProps } from './Menu';
|
||||||
|
|
||||||
const StyledHeader = styled.div`
|
const StyledHeader = styled.div`
|
||||||
margin-bottom: ${({ theme }) => theme.gridUnit * 4}px;
|
margin-bottom: ${({ theme }) => theme.gridUnit * 4}px;
|
||||||
@ -39,11 +41,21 @@ const StyledHeader = styled.div`
|
|||||||
.nav-right {
|
.nav-right {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 14px 0;
|
padding: ${({ theme }) => theme.gridUnit * 3.5}px 0;
|
||||||
margin-right: ${({ theme }) => theme.gridUnit * 3}px;
|
margin-right: ${({ theme }) => theme.gridUnit * 3}px;
|
||||||
float: right;
|
float: right;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
ul.ant-menu-root {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
li[role='menuitem'] {
|
||||||
|
border: 0;
|
||||||
|
border-bottom: none;
|
||||||
|
&:hover {
|
||||||
|
border-bottom: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.nav-right-collapse {
|
.nav-right-collapse {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -58,8 +70,10 @@ const StyledHeader = styled.div`
|
|||||||
.ant-menu-horizontal {
|
.ant-menu-horizontal {
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
.ant-menu-item {
|
.ant-menu-item {
|
||||||
|
border-bottom: none;
|
||||||
&:hover {
|
&:hover {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,6 +131,17 @@ const StyledHeader = styled.div`
|
|||||||
margin-left: ${({ theme }) => theme.gridUnit * 2}px;
|
margin-left: ${({ theme }) => theme.gridUnit * 2}px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.ant-menu-submenu {
|
||||||
|
span[role='img'] {
|
||||||
|
position: absolute;
|
||||||
|
right: ${({ theme }) => -theme.gridUnit + -2}px;
|
||||||
|
top: ${({ theme }) => theme.gridUnit + 1}px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.dropdown-menu-links > div.ant-menu-submenu-title,
|
||||||
|
.ant-menu-submenu-open.ant-menu-submenu-active > div.ant-menu-submenu-title {
|
||||||
|
color: ${({ theme }) => theme.colors.primary.dark1};
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type MenuChild = {
|
type MenuChild = {
|
||||||
@ -152,8 +177,11 @@ export interface SubMenuProps {
|
|||||||
* otherwise, a 'You should not use <Link> outside a <Router>' error will be thrown */
|
* otherwise, a 'You should not use <Link> outside a <Router>' error will be thrown */
|
||||||
usesRouter?: boolean;
|
usesRouter?: boolean;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
dropDownLinks?: Array<MenuObjectProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { SubMenu } = DropdownMenu;
|
||||||
|
|
||||||
const SubMenuComponent: React.FunctionComponent<SubMenuProps> = props => {
|
const SubMenuComponent: React.FunctionComponent<SubMenuProps> = props => {
|
||||||
const [showMenu, setMenu] = useState<MenuMode>('horizontal');
|
const [showMenu, setMenu] = useState<MenuMode>('horizontal');
|
||||||
const [navRightStyle, setNavRightStyle] = useState('nav-right');
|
const [navRightStyle, setNavRightStyle] = useState('nav-right');
|
||||||
@ -177,6 +205,7 @@ const SubMenuComponent: React.FunctionComponent<SubMenuProps> = props => {
|
|||||||
props.buttons.length >= 3 &&
|
props.buttons.length >= 3 &&
|
||||||
window.innerWidth >= 795
|
window.innerWidth >= 795
|
||||||
) {
|
) {
|
||||||
|
// eslint-disable-next-line no-unused-expressions
|
||||||
setNavRightStyle('nav-right');
|
setNavRightStyle('nav-right');
|
||||||
} else if (
|
} else if (
|
||||||
props.buttons &&
|
props.buttons &&
|
||||||
@ -231,6 +260,28 @@ const SubMenuComponent: React.FunctionComponent<SubMenuProps> = props => {
|
|||||||
})}
|
})}
|
||||||
</Menu>
|
</Menu>
|
||||||
<div className={navRightStyle}>
|
<div className={navRightStyle}>
|
||||||
|
<Menu mode="horizontal" triggerSubMenuAction="click">
|
||||||
|
{props.dropDownLinks?.map((link, i) => (
|
||||||
|
<SubMenu
|
||||||
|
key={i}
|
||||||
|
title={link.label}
|
||||||
|
icon={<Icons.TriangleDown />}
|
||||||
|
popupOffset={[10, 20]}
|
||||||
|
className="dropdown-menu-links"
|
||||||
|
>
|
||||||
|
{link.childs?.map(item => {
|
||||||
|
if (typeof item === 'object') {
|
||||||
|
return (
|
||||||
|
<DropdownMenu.Item key={item.label}>
|
||||||
|
<a href={item.url}>{item.label}</a>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</SubMenu>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
{props.buttons?.map((btn, i) => (
|
{props.buttons?.map((btn, i) => (
|
||||||
<Button
|
<Button
|
||||||
key={i}
|
key={i}
|
||||||
|
Loading…
Reference in New Issue
Block a user