mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Discover Tabs] Don't allow to duplicate a tab when tabs limit is reached (#214772)
## Summary This PR is a follow up for https://github.com/elastic/kibana/pull/213106 to hide Duplicate menu item when the max tabs limit is already reached. ## Testing Two options are possible: 1. start Storybook with `yarn storybook unified_tabs` and navigate to `http://localhost:9001`. 2. start Kibana with `yarn start --run-examples`. Then navigate to the Unified Tabs example plugin `http://localhost:5601/app/unifiedTabsExamples`. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
4f9c54f91b
commit
d764bd91f5
6 changed files with 121 additions and 18 deletions
|
@ -98,7 +98,7 @@ export const UnifiedTabsExampleApp: React.FC<UnifiedTabsExampleAppProps> = ({
|
|||
<div className="eui-fullHeight">
|
||||
<UnifiedTabs
|
||||
initialItems={initialItems}
|
||||
maxItemsCount={20}
|
||||
maxItemsCount={25}
|
||||
onChanged={() => {}}
|
||||
createItem={getNewTabDefaultProps}
|
||||
renderContent={({ label }) => {
|
||||
|
|
|
@ -101,16 +101,17 @@ export const TabbedContent: React.FC<TabbedContentProps> = ({
|
|||
const getTabMenuItems = useMemo(() => {
|
||||
return getTabMenuItemsFn({
|
||||
tabsState: state,
|
||||
maxItemsCount,
|
||||
onDuplicate: (item) => {
|
||||
const newItem = createItem();
|
||||
newItem.label = `${item.label} (copy)`;
|
||||
changeState((prevState) => insertTabAfter(prevState, newItem, item));
|
||||
changeState((prevState) => insertTabAfter(prevState, newItem, item, maxItemsCount));
|
||||
},
|
||||
onCloseOtherTabs: (item) => changeState((prevState) => closeOtherTabs(prevState, item)),
|
||||
onCloseTabsToTheRight: (item) =>
|
||||
changeState((prevState) => closeTabsToTheRight(prevState, item)),
|
||||
});
|
||||
}, [changeState, createItem, state]);
|
||||
}, [changeState, createItem, state, maxItemsCount]);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { getTabMenuItemsFn } from './get_tab_menu_items';
|
||||
import { getNewTabPropsForIndex } from '../hooks/use_new_tab_props';
|
||||
import type { TabMenuItem } from '../types';
|
||||
|
||||
const items = Array.from({ length: 5 }).map((_, i) => getNewTabPropsForIndex(i));
|
||||
|
||||
const mapMenuItem = (item: TabMenuItem) => {
|
||||
if (item === 'divider') {
|
||||
return 'divider';
|
||||
}
|
||||
return item.name;
|
||||
};
|
||||
|
||||
describe('getTabMenuItemsFn', () => {
|
||||
it('returns correct menu items for a single tab', () => {
|
||||
const getTabMenuItems = getTabMenuItemsFn({
|
||||
tabsState: { items: [items[0]], selectedItem: items[0] },
|
||||
maxItemsCount: 10,
|
||||
onDuplicate: jest.fn(),
|
||||
onCloseOtherTabs: jest.fn(),
|
||||
onCloseTabsToTheRight: jest.fn(),
|
||||
});
|
||||
const menuItems = getTabMenuItems(items[0]);
|
||||
expect(menuItems.map(mapMenuItem)).toEqual(['duplicate']);
|
||||
});
|
||||
|
||||
it('returns correct menu items for many tabs', () => {
|
||||
const getTabMenuItems = getTabMenuItemsFn({
|
||||
tabsState: { items, selectedItem: items[0] },
|
||||
maxItemsCount: 10,
|
||||
onDuplicate: jest.fn(),
|
||||
onCloseOtherTabs: jest.fn(),
|
||||
onCloseTabsToTheRight: jest.fn(),
|
||||
});
|
||||
const menuItems = getTabMenuItems(items[0]);
|
||||
expect(menuItems.map(mapMenuItem)).toEqual([
|
||||
'duplicate',
|
||||
'divider',
|
||||
'closeOtherTabs',
|
||||
'closeTabsToTheRight',
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns correct menu items when max limit is reached', () => {
|
||||
const getTabMenuItems = getTabMenuItemsFn({
|
||||
tabsState: { items, selectedItem: items[0] },
|
||||
maxItemsCount: items.length,
|
||||
onDuplicate: jest.fn(),
|
||||
onCloseOtherTabs: jest.fn(),
|
||||
onCloseTabsToTheRight: jest.fn(),
|
||||
});
|
||||
const menuItems = getTabMenuItems(items[2]);
|
||||
expect(menuItems.map(mapMenuItem)).toEqual(['closeOtherTabs', 'closeTabsToTheRight']);
|
||||
});
|
||||
|
||||
it('returns correct menu items for the last item of many tabs', () => {
|
||||
const getTabMenuItems = getTabMenuItemsFn({
|
||||
tabsState: { items, selectedItem: items[0] },
|
||||
maxItemsCount: 10,
|
||||
onDuplicate: jest.fn(),
|
||||
onCloseOtherTabs: jest.fn(),
|
||||
onCloseTabsToTheRight: jest.fn(),
|
||||
});
|
||||
const menuItems = getTabMenuItems(items[items.length - 1]);
|
||||
expect(menuItems.map(mapMenuItem)).toEqual(['duplicate', 'divider', 'closeOtherTabs']);
|
||||
});
|
||||
});
|
|
@ -34,6 +34,7 @@ const getTabMenuItem = ({
|
|||
|
||||
export interface GetTabMenuItemsFnProps {
|
||||
tabsState: TabsState;
|
||||
maxItemsCount: number | undefined;
|
||||
onDuplicate: (item: TabItem) => void;
|
||||
onCloseOtherTabs: (item: TabItem) => void;
|
||||
onCloseTabsToTheRight: (item: TabItem) => void;
|
||||
|
@ -41,6 +42,7 @@ export interface GetTabMenuItemsFnProps {
|
|||
|
||||
export const getTabMenuItemsFn = ({
|
||||
tabsState,
|
||||
maxItemsCount,
|
||||
onDuplicate,
|
||||
onCloseOtherTabs,
|
||||
onCloseTabsToTheRight,
|
||||
|
@ -68,19 +70,25 @@ export const getTabMenuItemsFn = ({
|
|||
onClick: onCloseTabsToTheRight,
|
||||
});
|
||||
|
||||
const items: TabMenuItem[] = [
|
||||
getTabMenuItem({
|
||||
item,
|
||||
name: 'duplicate',
|
||||
label: i18n.translate('unifiedTabs.tabMenu.duplicateMenuItem', {
|
||||
defaultMessage: 'Duplicate',
|
||||
}),
|
||||
onClick: onDuplicate,
|
||||
}),
|
||||
];
|
||||
const items: TabMenuItem[] = [];
|
||||
|
||||
if (!maxItemsCount || tabsState.items.length < maxItemsCount) {
|
||||
items.push(
|
||||
getTabMenuItem({
|
||||
item,
|
||||
name: 'duplicate',
|
||||
label: i18n.translate('unifiedTabs.tabMenu.duplicateMenuItem', {
|
||||
defaultMessage: 'Duplicate',
|
||||
}),
|
||||
onClick: onDuplicate,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (closeOtherTabsItem || closeTabsToTheRightItem) {
|
||||
items.push(DividerMenuItem);
|
||||
if (items.length > 0) {
|
||||
items.push(DividerMenuItem);
|
||||
}
|
||||
|
||||
if (closeOtherTabsItem) {
|
||||
items.push(closeOtherTabsItem);
|
||||
|
|
|
@ -120,17 +120,27 @@ describe('manage_tabs', () => {
|
|||
it('inserts a tab after another tab', () => {
|
||||
const newItem = { id: 'tab-5', label: 'Tab 5' };
|
||||
const prevState = { items, selectedItem: items[0] };
|
||||
const nextState = insertTabAfter(prevState, newItem, items[2]);
|
||||
const nextState = insertTabAfter(prevState, newItem, items[2], undefined);
|
||||
|
||||
expect(nextState.items).not.toBe(items);
|
||||
expect(nextState.items).toEqual([items[0], items[1], items[2], newItem, items[3], items[4]]);
|
||||
expect(nextState.selectedItem).toBe(newItem);
|
||||
});
|
||||
|
||||
it('should not insert a tab if the limit is reached', () => {
|
||||
const maxItemsCount = items.length;
|
||||
const newItem = { id: 'tab-5', label: 'Tab 5' };
|
||||
const prevState = { items, selectedItem: items[0] };
|
||||
const nextState = insertTabAfter(prevState, newItem, items[2], maxItemsCount);
|
||||
|
||||
expect(nextState.items).toBe(items);
|
||||
expect(nextState.selectedItem).toBe(items[0]);
|
||||
});
|
||||
|
||||
it('inserts a tab after the last tab', () => {
|
||||
const newItem = { id: 'tab-5', label: 'Tab 5' };
|
||||
const prevState = { items, selectedItem: items[0] };
|
||||
const nextState = insertTabAfter(prevState, newItem, items[items.length - 1]);
|
||||
const nextState = insertTabAfter(prevState, newItem, items[items.length - 1], 100);
|
||||
|
||||
expect(nextState.items).not.toBe(items);
|
||||
expect(nextState.items).toEqual([items[0], items[1], items[2], items[3], items[4], newItem]);
|
||||
|
|
|
@ -25,7 +25,7 @@ export const isLastTab = ({ items }: TabsState, item: TabItem): boolean => {
|
|||
export const addTab = (
|
||||
{ items, selectedItem }: TabsState,
|
||||
item: TabItem,
|
||||
maxItemsCount?: number
|
||||
maxItemsCount: number | undefined
|
||||
): TabsState => {
|
||||
if (maxItemsCount && items.length >= maxItemsCount) {
|
||||
return {
|
||||
|
@ -79,8 +79,16 @@ export const closeTab = ({ items, selectedItem }: TabsState, item: TabItem): Tab
|
|||
export const insertTabAfter = (
|
||||
{ items, selectedItem }: TabsState,
|
||||
item: TabItem,
|
||||
insertAfterItem: TabItem
|
||||
insertAfterItem: TabItem,
|
||||
maxItemsCount: number | undefined
|
||||
): TabsState => {
|
||||
if (maxItemsCount && items.length >= maxItemsCount) {
|
||||
return {
|
||||
items,
|
||||
selectedItem,
|
||||
};
|
||||
}
|
||||
|
||||
const insertAfterIndex = items.findIndex((i) => i.id === insertAfterItem.id);
|
||||
|
||||
if (insertAfterIndex === -1) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue