mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-30 11:26:23 +00:00
Fix save enable/disable on customize navigation page (#23142)
* Fix save enable/disable on customize navigation page * add e2e tests * fix failing and flaky tests * address comments * Address comment and fix curated assets e2e tests
This commit is contained in:
parent
8177e529bc
commit
57d0d70e99
@ -266,7 +266,7 @@ test.describe('Curated Assets Widget', () => {
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator.locator('.rule--operator .ant-select'),
|
||||
'=='
|
||||
'Is'
|
||||
);
|
||||
|
||||
await ruleLocator
|
||||
@ -340,7 +340,7 @@ test.describe('Curated Assets Widget', () => {
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator1.locator('.rule--operator .ant-select'),
|
||||
'Is not null'
|
||||
'Is Set'
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: 'Add Condition' }).click();
|
||||
@ -357,7 +357,7 @@ test.describe('Curated Assets Widget', () => {
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator2.locator('.rule--operator .ant-select'),
|
||||
'=='
|
||||
'Is'
|
||||
);
|
||||
await ruleLocator2
|
||||
.locator('.rule--value .rule--widget--BOOLEAN .ant-switch')
|
||||
@ -444,7 +444,7 @@ test.describe('Curated Assets Widget', () => {
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator1.locator('.rule--operator .ant-select'),
|
||||
'=='
|
||||
'Is'
|
||||
);
|
||||
await ruleLocator1
|
||||
.locator('.rule--value .rule--widget--BOOLEAN .ant-switch')
|
||||
@ -570,7 +570,8 @@ test.describe('Curated Assets Widget', () => {
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator1.locator('.rule--value .ant-select'),
|
||||
'admin'
|
||||
'admin',
|
||||
true
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: 'Add Condition' }).click();
|
||||
@ -588,7 +589,7 @@ test.describe('Curated Assets Widget', () => {
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator2.locator('.rule--operator .ant-select'),
|
||||
'=='
|
||||
'Is'
|
||||
);
|
||||
await selectOption(
|
||||
page,
|
||||
@ -610,7 +611,7 @@ test.describe('Curated Assets Widget', () => {
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator3.locator('.rule--operator .ant-select'),
|
||||
'!='
|
||||
'Is Not'
|
||||
);
|
||||
await selectOption(
|
||||
page,
|
||||
|
@ -126,7 +126,7 @@ test.describe('Navigation Blocker Tests', () => {
|
||||
await expect(adminPage.locator('.ant-modal')).toBeVisible();
|
||||
|
||||
// Click "Save changes" button (should save changes and then navigate)
|
||||
const saveResponse = adminPage.waitForResponse('api/v1/docStore/**');
|
||||
const saveResponse = adminPage.waitForResponse('api/v1/docStore*');
|
||||
await adminPage.locator('button:has-text("Save changes")').click();
|
||||
|
||||
// Wait for save operation to complete
|
||||
|
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed 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 { expect, Page, test as base } from '@playwright/test';
|
||||
import { GlobalSettingOptions } from '../../constant/settings';
|
||||
import { PersonaClass } from '../../support/persona/PersonaClass';
|
||||
import { UserClass } from '../../support/user/UserClass';
|
||||
import { performAdminLogin } from '../../utils/admin';
|
||||
import { redirectToHomePage } from '../../utils/common';
|
||||
import { setUserDefaultPersona } from '../../utils/customizeLandingPage';
|
||||
import { settingClick } from '../../utils/sidebar';
|
||||
|
||||
const adminUser = new UserClass();
|
||||
const persona = new PersonaClass();
|
||||
|
||||
const test = base.extend<{ page: Page }>({
|
||||
page: async ({ browser }, use) => {
|
||||
const page = await browser.newPage();
|
||||
await adminUser.login(page);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
});
|
||||
|
||||
base.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
const { afterAction, apiContext } = await performAdminLogin(browser);
|
||||
await adminUser.create(apiContext);
|
||||
await adminUser.setAdminRole(apiContext);
|
||||
await persona.create(apiContext, [adminUser.responseData.id]);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
const navigateToPersonaNavigation = async (page: Page) => {
|
||||
const getPersonas = page.waitForResponse('/api/v1/personas*');
|
||||
await settingClick(page, GlobalSettingOptions.PERSONA);
|
||||
await page.waitForLoadState('networkidle');
|
||||
await getPersonas;
|
||||
|
||||
await page
|
||||
.getByTestId(`persona-details-card-${persona.responseData.name}`)
|
||||
.click();
|
||||
|
||||
await page.getByTestId('navigation').click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
};
|
||||
|
||||
test.describe('Settings Navigation Page Tests', () => {
|
||||
test('should update navigation sidebar', async ({ page }) => {
|
||||
// Create and set default persona
|
||||
await redirectToHomePage(page);
|
||||
await setUserDefaultPersona(page, persona.responseData.displayName);
|
||||
|
||||
// Go to navigation in persona
|
||||
await navigateToPersonaNavigation(page);
|
||||
|
||||
// Verify page loads with expected elements
|
||||
await expect(page.getByRole('tree')).toBeVisible();
|
||||
await expect(page.getByTestId('save-button')).toBeVisible();
|
||||
await expect(page.getByTestId('reset-button')).toBeVisible();
|
||||
|
||||
// Save button should be disabled initially
|
||||
await expect(page.getByTestId('save-button')).toBeEnabled();
|
||||
|
||||
// Make changes to enable save button
|
||||
const exploreSwitch = page
|
||||
.locator('.ant-tree-title:has-text("Explore")')
|
||||
.locator('.ant-switch');
|
||||
|
||||
await exploreSwitch.click();
|
||||
|
||||
// Check save is enabled and click save
|
||||
await expect(page.getByTestId('save-button')).toBeEnabled();
|
||||
|
||||
const saveResponse = page.waitForResponse('api/v1/docStore');
|
||||
await page.getByTestId('save-button').click();
|
||||
await saveResponse;
|
||||
|
||||
// Check the navigation bar if the changes reflect
|
||||
await redirectToHomePage(page);
|
||||
|
||||
// Verify the navigation change is reflected in the sidebar
|
||||
await expect(page.getByTestId('app-bar-item-explore')).not.toBeVisible();
|
||||
|
||||
// Clean up: Restore original state
|
||||
await navigateToPersonaNavigation(page);
|
||||
await exploreSwitch.click();
|
||||
|
||||
const restoreResponse = page.waitForResponse('api/v1/docStore/*');
|
||||
await page.getByTestId('save-button').click();
|
||||
await restoreResponse;
|
||||
});
|
||||
|
||||
test('should show navigation blocker when leaving with unsaved changes', async ({
|
||||
page,
|
||||
}) => {
|
||||
// Create persona and navigate to navigation page
|
||||
await redirectToHomePage(page);
|
||||
await setUserDefaultPersona(page, persona.responseData.displayName);
|
||||
await navigateToPersonaNavigation(page);
|
||||
|
||||
// Make changes to trigger unsaved state
|
||||
const navigateSwitch = page
|
||||
.locator('.ant-tree-title:has-text("Explore")')
|
||||
.locator('.ant-switch');
|
||||
|
||||
await navigateSwitch.click();
|
||||
|
||||
// Verify save button is enabled
|
||||
await expect(page.getByTestId('save-button')).toBeEnabled();
|
||||
|
||||
// Try to navigate away - should show navigation blocker
|
||||
await page
|
||||
.getByTestId('left-sidebar')
|
||||
.getByTestId('app-bar-item-settings')
|
||||
.click();
|
||||
|
||||
// Verify navigation blocker modal appears
|
||||
await expect(page.getByTestId('unsaved-changes-modal-title')).toContainText(
|
||||
'Unsaved changes'
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId('unsaved-changes-modal-description')
|
||||
).toContainText('Do you want to save or discard changes?');
|
||||
|
||||
// Verify modal buttons
|
||||
await expect(page.getByTestId('unsaved-changes-modal-save')).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId('unsaved-changes-modal-discard')
|
||||
).toBeVisible();
|
||||
|
||||
// Test discard changes
|
||||
await page.getByTestId('unsaved-changes-modal-discard').click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Should navigate away and changes should be discarded
|
||||
await expect(page).toHaveURL(/.*settings.*/);
|
||||
});
|
||||
|
||||
test('should save changes and navigate when "Save changes" is clicked in blocker', async ({
|
||||
page,
|
||||
}) => {
|
||||
// Create persona and navigate to navigation page
|
||||
await redirectToHomePage(page);
|
||||
await setUserDefaultPersona(page, persona.responseData.displayName);
|
||||
await navigateToPersonaNavigation(page);
|
||||
|
||||
// Make changes
|
||||
const navigateSwitch = page
|
||||
.locator('.ant-tree-title:has-text("Insights")')
|
||||
.locator('.ant-switch');
|
||||
|
||||
await navigateSwitch.click();
|
||||
|
||||
// Try to navigate away
|
||||
await page
|
||||
.getByTestId('left-sidebar')
|
||||
.getByTestId('app-bar-item-settings')
|
||||
.click();
|
||||
|
||||
// Click "Save changes" to save and navigate
|
||||
const saveResponse = page.waitForResponse('api/v1/docStore');
|
||||
await page.getByTestId('unsaved-changes-modal-save').click();
|
||||
await saveResponse;
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Should navigate to settings page
|
||||
await expect(page).toHaveURL(/.*settings.*/);
|
||||
|
||||
// Verify changes were saved by checking navigation bar
|
||||
await redirectToHomePage(page);
|
||||
|
||||
// Check if Insights navigation item visibility changed
|
||||
const insightsVisible = await page
|
||||
.getByTestId('left-sidebar')
|
||||
.getByTestId('app-bar-item-insights')
|
||||
.isVisible();
|
||||
|
||||
expect(insightsVisible).toBe(false);
|
||||
|
||||
// Clean up: Restore original state
|
||||
await navigateToPersonaNavigation(page);
|
||||
await navigateSwitch.click();
|
||||
|
||||
const restoreResponse = page.waitForResponse('api/v1/docStore/*');
|
||||
await page.getByTestId('save-button').click();
|
||||
await restoreResponse;
|
||||
});
|
||||
|
||||
test('should handle reset functionality and prevent navigation blocker after save', async ({
|
||||
page,
|
||||
}) => {
|
||||
// Create persona and navigate to navigation page
|
||||
await redirectToHomePage(page);
|
||||
await setUserDefaultPersona(page, persona.responseData.displayName);
|
||||
await navigateToPersonaNavigation(page);
|
||||
|
||||
// Make changes
|
||||
const domainSwitch = page
|
||||
.locator('.ant-tree-title:has-text("Domains")')
|
||||
.locator('.ant-switch');
|
||||
|
||||
await domainSwitch.click();
|
||||
|
||||
// Verify save button is enabled
|
||||
await expect(page.getByTestId('save-button')).toBeEnabled();
|
||||
|
||||
expect(await domainSwitch.isChecked()).toBeFalsy();
|
||||
|
||||
// Test reset functionality
|
||||
await page.getByTestId('reset-button').click();
|
||||
|
||||
// Verify navigation blocker modal appears
|
||||
await expect(page.getByTestId('unsaved-changes-modal-title')).toContainText(
|
||||
'Reset Default Layout'
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId('unsaved-changes-modal-description')
|
||||
).toContainText('Are you sure you want to apply the "Default Layout"?');
|
||||
|
||||
// Verify modal buttons
|
||||
await expect(page.getByTestId('unsaved-changes-modal-save')).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId('unsaved-changes-modal-discard')
|
||||
).toBeVisible();
|
||||
|
||||
// Test discard changes
|
||||
await page.getByTestId('unsaved-changes-modal-save').click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Verify reset worked - save button disabled and state reverted
|
||||
expect(await domainSwitch.isChecked()).toBeTruthy();
|
||||
await expect(page.getByTestId('save-button')).toBeEnabled();
|
||||
});
|
||||
});
|
@ -33,6 +33,7 @@ export const UnsavedChangesModal: React.FC<UnsavedChangesModalProps> = ({
|
||||
centered
|
||||
closable
|
||||
className="unsaved-changes-modal-container"
|
||||
data-testid="unsaved-changes-modal"
|
||||
footer={null}
|
||||
open={open}
|
||||
width={400}
|
||||
@ -44,21 +45,30 @@ export const UnsavedChangesModal: React.FC<UnsavedChangesModalProps> = ({
|
||||
</div>
|
||||
|
||||
<div className="unsaved-changes-modal-content">
|
||||
<Typography.Title className="unsaved-changes-modal-title" level={5}>
|
||||
<Typography.Title
|
||||
className="unsaved-changes-modal-title"
|
||||
data-testid="unsaved-changes-modal-title"
|
||||
level={5}>
|
||||
{title}
|
||||
</Typography.Title>
|
||||
<Typography.Text className="unsaved-changes-modal-description">
|
||||
<Typography.Text
|
||||
className="unsaved-changes-modal-description"
|
||||
data-testid="unsaved-changes-modal-description">
|
||||
{description}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="unsaved-changes-modal-actions">
|
||||
<Button className="unsaved-changes-modal-discard" onClick={onDiscard}>
|
||||
<Button
|
||||
className="unsaved-changes-modal-discard"
|
||||
data-testid="unsaved-changes-modal-discard"
|
||||
onClick={onDiscard}>
|
||||
{discardText}
|
||||
</Button>
|
||||
<Button
|
||||
className="unsaved-changes-modal-save"
|
||||
data-testid="unsaved-changes-modal-save"
|
||||
loading={loading}
|
||||
type="primary"
|
||||
onClick={onSave}>
|
||||
|
@ -77,7 +77,7 @@ export const mockCustomizedLayout1: Array<WidgetConfig> = [
|
||||
{
|
||||
h: 3,
|
||||
i: LandingPageWidgetKeys.ACTIVITY_FEED,
|
||||
w: 3,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
static: false,
|
||||
@ -104,7 +104,7 @@ export const mockCustomizedLayout2: Array<WidgetConfig> = [
|
||||
{
|
||||
h: 6,
|
||||
i: LandingPageWidgetKeys.ACTIVITY_FEED,
|
||||
w: 3,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
static: false,
|
||||
@ -158,7 +158,7 @@ export const mockCurrentAddWidget = [
|
||||
{
|
||||
h: 3,
|
||||
i: 'KnowledgePanel.ActivityFeed',
|
||||
w: 3,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
static: false,
|
||||
@ -174,7 +174,7 @@ export const mockCurrentAddWidget = [
|
||||
{
|
||||
h: 3,
|
||||
i: 'ExtraWidget.EmptyWidgetPlaceholder',
|
||||
w: 4,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 6,
|
||||
isDraggable: false,
|
||||
@ -187,7 +187,7 @@ export const mockAddWidgetReturnValues = [
|
||||
h: 3,
|
||||
i: 'KnowledgePanel.ActivityFeed',
|
||||
static: false,
|
||||
w: 3,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
@ -204,7 +204,7 @@ export const mockAddWidgetReturnValues = [
|
||||
i: 'ExtraWidget.EmptyWidgetPlaceholder',
|
||||
isDraggable: false,
|
||||
static: false,
|
||||
w: 4,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 100,
|
||||
},
|
||||
@ -216,7 +216,7 @@ export const mockAddWidgetReturnValues2 = [
|
||||
h: 3,
|
||||
i: 'KnowledgePanel.ActivityFeed',
|
||||
static: false,
|
||||
w: 3,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
@ -233,7 +233,7 @@ export const mockAddWidgetReturnValues2 = [
|
||||
i: 'ExtraWidget.EmptyWidgetPlaceholder',
|
||||
isDraggable: false,
|
||||
static: false,
|
||||
w: 4,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 100,
|
||||
},
|
||||
|
@ -38,7 +38,7 @@ export const mockDefaultLayout: Array<WidgetConfig> = [
|
||||
{
|
||||
h: 6,
|
||||
i: LandingPageWidgetKeys.ACTIVITY_FEED,
|
||||
w: 3,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
static: false,
|
||||
@ -62,7 +62,7 @@ export const mockDefaultLayout: Array<WidgetConfig> = [
|
||||
{
|
||||
h: 3,
|
||||
i: LandingPageWidgetKeys.TOTAL_DATA_ASSETS,
|
||||
w: 3,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 9,
|
||||
static: false,
|
||||
@ -89,7 +89,7 @@ export const mockCustomizedLayout: Array<WidgetConfig> = [
|
||||
{
|
||||
h: 6,
|
||||
i: LandingPageWidgetKeys.ACTIVITY_FEED,
|
||||
w: 3,
|
||||
w: 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
static: false,
|
||||
|
@ -18,6 +18,7 @@ import { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as IconDown } from '../../assets/svg/ic-arrow-down.svg';
|
||||
import { ReactComponent as IconRight } from '../../assets/svg/ic-arrow-right.svg';
|
||||
import { NavigationBlocker } from '../../components/common/NavigationBlocker/NavigationBlocker';
|
||||
import { CustomizablePageHeader } from '../../components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader';
|
||||
import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
|
||||
import { NavigationItem } from '../../generated/system/ui/uiCustomization';
|
||||
@ -46,20 +47,11 @@ export const SettingsNavigationPage = ({ onSave }: Props) => {
|
||||
);
|
||||
|
||||
const disableSave = useMemo(() => {
|
||||
// Get the initial hidden keys from the current navigation
|
||||
const initialHiddenKeys =
|
||||
getHiddenKeysFromNavigationItems(currentNavigation);
|
||||
// Get the current navigation items from the modified tree data
|
||||
const currentNavigationItems = getNavigationItems(treeData, hiddenKeys);
|
||||
|
||||
// Get the current navigation items from the tree data
|
||||
const currentNavigationItems =
|
||||
getNavigationItems(treeData, hiddenKeys) || [];
|
||||
|
||||
// Get the current hidden keys from the current navigation items
|
||||
const currentHiddenKeys =
|
||||
getHiddenKeysFromNavigationItems(currentNavigationItems) || [];
|
||||
|
||||
// Check if the initial hidden keys are the same as the current hidden keys
|
||||
return isEqual(initialHiddenKeys, currentHiddenKeys);
|
||||
// Compare the entire structure including order, names, hidden status, and all properties
|
||||
return isEqual(currentNavigation, currentNavigationItems);
|
||||
}, [currentNavigation, treeData, hiddenKeys]);
|
||||
|
||||
const handleSave = async () => {
|
||||
@ -149,37 +141,39 @@ export const SettingsNavigationPage = ({ onSave }: Props) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<PageLayoutV1 className="bg-grey" pageTitle="Settings Navigation Page">
|
||||
<Row gutter={[0, 20]}>
|
||||
<Col span={24}>
|
||||
<CustomizablePageHeader
|
||||
disableSave={disableSave}
|
||||
personaName={t('label.customize-your-navigation')}
|
||||
onReset={handleReset}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col span={24}>
|
||||
<Card
|
||||
bordered={false}
|
||||
className="custom-navigation-tree-container"
|
||||
title="Navigation Menus">
|
||||
<Tree
|
||||
autoExpandParent
|
||||
blockNode
|
||||
defaultExpandAll
|
||||
showIcon
|
||||
draggable={{ icon: <HolderOutlined /> }}
|
||||
itemHeight={48}
|
||||
switcherIcon={switcherIcon}
|
||||
titleRender={titleRenderer}
|
||||
treeData={treeData}
|
||||
onDrop={onDrop}
|
||||
<NavigationBlocker enabled={!disableSave} onConfirm={handleSave}>
|
||||
<PageLayoutV1 className="bg-grey" pageTitle="Settings Navigation Page">
|
||||
<Row gutter={[0, 20]}>
|
||||
<Col span={24}>
|
||||
<CustomizablePageHeader
|
||||
disableSave={disableSave}
|
||||
personaName={t('label.customize-your-navigation')}
|
||||
onReset={handleReset}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</PageLayoutV1>
|
||||
</Col>
|
||||
|
||||
<Col span={24}>
|
||||
<Card
|
||||
bordered={false}
|
||||
className="custom-navigation-tree-container"
|
||||
title="Navigation Menus">
|
||||
<Tree
|
||||
autoExpandParent
|
||||
blockNode
|
||||
defaultExpandAll
|
||||
showIcon
|
||||
draggable={{ icon: <HolderOutlined /> }}
|
||||
itemHeight={48}
|
||||
switcherIcon={switcherIcon}
|
||||
titleRender={titleRenderer}
|
||||
treeData={treeData}
|
||||
onDrop={onDrop}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</PageLayoutV1>
|
||||
</NavigationBlocker>
|
||||
);
|
||||
};
|
||||
|
@ -575,7 +575,41 @@ class AdvancedSearchClassBase {
|
||||
...this.baseConfig,
|
||||
types: this.configTypes,
|
||||
widgets: this.configWidgets,
|
||||
operators: this.configOperators,
|
||||
operators: {
|
||||
...this.configOperators,
|
||||
like: {
|
||||
...this.baseConfig.operators.like,
|
||||
elasticSearchQueryType: 'wildcard',
|
||||
},
|
||||
...(isExplorePage
|
||||
? {}
|
||||
: {
|
||||
equal: {
|
||||
...this.baseConfig.operators.equal,
|
||||
label: t('label.is'),
|
||||
},
|
||||
not_equal: {
|
||||
...this.baseConfig.operators.not_equal,
|
||||
label: t('label.is-not'),
|
||||
},
|
||||
select_equals: {
|
||||
...this.baseConfig.operators.select_equals,
|
||||
label: t('label.is'),
|
||||
},
|
||||
select_not_equals: {
|
||||
...this.baseConfig.operators.select_not_equals,
|
||||
label: t('label.is-not'),
|
||||
},
|
||||
is_null: {
|
||||
...this.baseConfig.operators.is_null,
|
||||
label: t('label.is-not-set'),
|
||||
},
|
||||
is_not_null: {
|
||||
...this.baseConfig.operators.is_not_null,
|
||||
label: t('label.is-set'),
|
||||
},
|
||||
}),
|
||||
},
|
||||
settings: {
|
||||
...this.baseConfig.settings,
|
||||
showLabels: isExplorePage,
|
||||
|
@ -23,6 +23,15 @@ import { Document } from '../generated/entity/docStore/document';
|
||||
import { WidgetConfig } from '../pages/CustomizablePage/CustomizablePage.interface';
|
||||
import customizeMyDataPageClassBase from './CustomizeMyDataPageClassBase';
|
||||
|
||||
/**
|
||||
* Ensures widget width doesn't exceed the maximum allowed width of 2
|
||||
*/
|
||||
export const getConstrainedWidgetWidth = (width: number): number => {
|
||||
const maxWidth = 2;
|
||||
|
||||
return Math.min(width, maxWidth);
|
||||
};
|
||||
|
||||
export const getNewWidgetPlacement = (
|
||||
currentLayout: WidgetConfig[],
|
||||
widgetWidth: number
|
||||
@ -284,16 +293,18 @@ export const getAddWidgetHandler =
|
||||
);
|
||||
|
||||
if (!currentLayout || currentLayout.length === 0) {
|
||||
const constrainedWidth = getConstrainedWidgetWidth(widgetWidth);
|
||||
|
||||
return [
|
||||
{
|
||||
w: widgetWidth,
|
||||
w: constrainedWidth,
|
||||
h: widgetHeight,
|
||||
i: widgetFQN,
|
||||
static: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
createPlaceholderWidget(widgetWidth, 0),
|
||||
createPlaceholderWidget(constrainedWidth, 0),
|
||||
];
|
||||
}
|
||||
|
||||
@ -306,8 +317,9 @@ export const getAddWidgetHandler =
|
||||
// Calculate position at the end of all existing widgets
|
||||
const placement = getNewWidgetPlacement(regularWidgets, widgetWidth);
|
||||
|
||||
const constrainedWidth = getConstrainedWidgetWidth(widgetWidth);
|
||||
const newWidget = {
|
||||
w: widgetWidth,
|
||||
w: constrainedWidth,
|
||||
h: widgetHeight,
|
||||
i: widgetFQN,
|
||||
static: false,
|
||||
@ -319,14 +331,15 @@ export const getAddWidgetHandler =
|
||||
}
|
||||
|
||||
// Replace specific placeholder
|
||||
const constrainedWidth = getConstrainedWidgetWidth(widgetWidth);
|
||||
const updatedWidgets = currentLayout.map((widget: WidgetConfig) => {
|
||||
if (widget.i === placeholderWidgetKey) {
|
||||
return {
|
||||
...widget,
|
||||
i: widgetFQN,
|
||||
h: widgetHeight,
|
||||
w: widgetWidth,
|
||||
x: Math.min(widget.x, maxGridSize - widgetWidth),
|
||||
w: constrainedWidth,
|
||||
x: Math.min(widget.x, maxGridSize - constrainedWidth),
|
||||
static: false,
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user