diff --git a/packages/ui/Release.md b/packages/ui/Release.md index af17252dd..7f666c64d 100644 --- a/packages/ui/Release.md +++ b/packages/ui/Release.md @@ -1,3 +1,6 @@ +### 8.2.1 2024-02-02 +- fix: SJIP-680 fix config corrumption when resizing the browser + ### 8.2.1 2024-02-01 - fix: SKFP-934 fix ok button text on cavatica analyse modal diff --git a/packages/ui/package-lock.json b/packages/ui/package-lock.json index 4ef4e11de..c8b096770 100644 --- a/packages/ui/package-lock.json +++ b/packages/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ferlab/ui", - "version": "8.2.2", + "version": "8.2.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ferlab/ui", - "version": "8.2.2", + "version": "8.2.3", "license": "SEE LICENSE IN LICENSE", "dependencies": { "@ant-design/icons": "^4.7.0", @@ -26,7 +26,7 @@ "less2sass": "^1.0.3", "md5": "^2.3.0", "query-string": "^7.0.1", - "react-grid-layout": "^1.3.4", + "react-grid-layout": "^1.4.4", "react-icons": "^4.2.0", "react-sizeme": "^3.0.2", "simplebar-react": "^2.4.3", @@ -78,6 +78,7 @@ "peerDependencies": { "antd": "^4.24.11", "date-fns": "^2.29.3", + "rc-tree-select": "^5.4.0", "react": "^18.2.0", "react-dom": "^18.2.0" } @@ -3430,14 +3431,6 @@ "node": ">=12" } }, - "node_modules/clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", - "engines": { - "node": ">=6" - } - }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -9107,6 +9100,14 @@ "react-dom": ">= 16.3.0" } }, + "node_modules/react-grid-layout/node_modules/clsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", + "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "engines": { + "node": ">=6" + } + }, "node_modules/react-icons": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", diff --git a/packages/ui/package.json b/packages/ui/package.json index 1fe044452..531eb9c99 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@ferlab/ui", - "version": "8.2.2", + "version": "8.2.3", "description": "Core components for scientific research data portals", "publishConfig": { "access": "public" @@ -82,6 +82,7 @@ "peerDependencies": { "antd": "^4.24.11", "date-fns": "^2.29.3", + "rc-tree-select": "^5.4.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -112,7 +113,7 @@ "less2sass": "^1.0.3", "md5": "^2.3.0", "query-string": "^7.0.1", - "react-grid-layout": "^1.3.4", + "react-grid-layout": "^1.4.4", "react-icons": "^4.2.0", "react-sizeme": "^3.0.2", "simplebar-react": "^2.4.3", diff --git a/packages/ui/src/layout/ResizableGridLayout/ResizableGridCard/index.tsx b/packages/ui/src/layout/ResizableGridLayout/ResizableGridCard/index.tsx index b6de915cf..1c8f6e413 100644 --- a/packages/ui/src/layout/ResizableGridLayout/ResizableGridCard/index.tsx +++ b/packages/ui/src/layout/ResizableGridLayout/ResizableGridCard/index.tsx @@ -70,7 +70,7 @@ const EXPORT_SETTINGS = { scale: 1, }; -const CARD_HEADER_TITLE_TRUNCATE_THRESHOLD_WIDTH = 75; +const CARD_HEADER_TITLE_TRUNCATE_THRESHOLD_WIDTH = 140; const DOWNLOAD_DELAY = 250; const DEFAULT_TSV_HEADERS = ['Value', 'Count', 'Frequency']; const DEFAULT_TSV_CONTENT_MAP = ['label', 'value', 'frequency']; diff --git a/packages/ui/src/layout/ResizableGridLayout/index.test.tsx b/packages/ui/src/layout/ResizableGridLayout/index.test.tsx index e17559e4b..dc5f74523 100644 --- a/packages/ui/src/layout/ResizableGridLayout/index.test.tsx +++ b/packages/ui/src/layout/ResizableGridLayout/index.test.tsx @@ -6,7 +6,6 @@ import GridCard, { GridCardHeader } from '../../view/v2/GridCard'; import { deserialize, hasLayout, - isLayoutConfigEqual, isPrisitine, serialize, serializeConfigToLayouts, @@ -515,123 +514,4 @@ describe('ResizableGridLayout', () => { expect(isPrisitine(defaultConfigs, configs)).toBeTruthy(); expect(isPrisitine(defaultConfigs, configs2)).toBeFalsy(); }); - - test('isLayoutConfigEqual can compare configs to allLayouts object', () => { - const layouts = { - lg: [ - { - h: 3, - i: 'card_1', - minH: 3, - minW: 3, - w: 6, - x: 0, - y: 0, - }, - ], - md: [ - { - h: 10, - i: 'card_1', - isBounded: undefined, - isDraggable: undefined, - isResizable: undefined, - maxH: undefined, - maxW: undefined, - minH: 3, - minW: 3, - moved: false, - resizeHandles: undefined, - static: false, - w: 10, - x: 10, - y: 0, - }, - ], - sm: [ - { - h: 10, - i: 'card_1', - minH: 3, - minW: 3, - w: 10, - x: 10, - y: 0, - }, - ], - xs: [ - { - h: 3, - i: 'card_1', - minH: 3, - minW: 3, - w: 6, - x: 0, - y: 0, - }, - ], - xxs: [ - { - h: 3, - i: 'card_1', - minH: 3, - minW: 3, - w: 6, - x: 0, - y: 0, - }, - ], - }; - - const config1 = [ - { - base: { - h: 3, - minh: 3, - minw: 3, - w: 6, - x: 0, - y: 0, - }, - component: , - id: 'card_1', - md: { - h: 10, - moved: false, // default value added by RCL - static: false, // default value added by RCL - w: 10, - x: 10, - y: 0, - }, - title: 'card 1', - }, - ]; - - const config2 = [ - { - base: { - h: 3, - minh: 3, - minw: 3, - w: 6, - x: 0, - y: 0, - }, - component: , - id: 'card_1', - md: { - h: 4, - moved: false, // default value added by RCL - static: false, // default value added by RCL - w: 6, - x: 6, - y: 0, - }, - title: 'card 1', - }, - ]; - - expect(isLayoutConfigEqual(layouts, config1)).toBeTruthy(); - expect(isLayoutConfigEqual(layouts, config2)).toBeFalsy(); - }); }); diff --git a/packages/ui/src/layout/ResizableGridLayout/index.tsx b/packages/ui/src/layout/ResizableGridLayout/index.tsx index 7f77ed04a..887e8d49b 100644 --- a/packages/ui/src/layout/ResizableGridLayout/index.tsx +++ b/packages/ui/src/layout/ResizableGridLayout/index.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useEffect, useState } from 'react'; +import React, { createContext, memo, useEffect, useState } from 'react'; import { Layout, Layouts, Responsive as ResponsiveGridLayout, ResponsiveProps } from 'react-grid-layout'; import { SizeMe } from 'react-sizeme'; import { Space, Spin } from 'antd'; @@ -159,43 +159,6 @@ export const serializeLayoutsToConfig = ( ); }; -/** - * Compare config - * @param layouts - * @param configs - * @returns boolean - */ -export const isLayoutConfigEqual = (layouts: Layouts, configs: IResizableGridLayoutConfig[]): boolean => { - const serializedConfigs = serialize(configs); - const serializedAllLayouts = serializeLayoutsToConfig(layouts, configs); - - for (const config of serializedConfigs) { - for (const layout of serializedAllLayouts) { - if (layout.id == config.id) { - for (const breakpoint in BREAKPOINTS) { - const configValue = config[ - breakpoint as keyof TSerializedResizableGridLayoutConfig - ] as ILayoutItemConfig; - - const layoutValue = layout[ - breakpoint as keyof TSerializedResizableGridLayoutConfig - ] as ILayoutItemConfig; - - for (const property in configValue) { - if ( - configValue[property as keyof ILayoutItemConfig] !== - layoutValue[property as keyof ILayoutItemConfig] - ) { - return false; - } - } - } - } - } - } - return true; -}; - /** * Iterate over all react-grid layouts to return a specific * layout or undefined if the layout doesn't exist @@ -323,7 +286,10 @@ export const generateOptionalBaseConfig = (base: ILayoutItemConfig): TOptionalBa return optionalBaseValues; }; -const ResizableGridLayout = ({ +const arePropsEqual = (oldProps: IResizableGridLayout, newProps: IResizableGridLayout) => + JSON.stringify(oldProps.layouts) === JSON.stringify(newProps.layouts); + +const ResizableGridLayout = memo(function ResizableGridLayout({ defaultLayouts, dictionary, layouts, @@ -331,11 +297,15 @@ const ResizableGridLayout = ({ onReset, uid, ...props -}: IResizableGridLayout): JSX.Element => { +}: IResizableGridLayout) { const [currentBreakpoint, setCurrentBreakpoint] = useState(undefined); const [isLoaded, setIsLoaded] = useState(false); const configs = deserialize(defaultLayouts, layouts); const responsiveDefaultLayouts = serializeConfigToLayouts(configs); + // React-grid-layout lifecycle is pretty tricky + // It will trigger onLayoutChange before updating the breakpoint + // Result in data of md are sending to lg. Thas why this flag is needed + const [hasLayoutChanged, setHasLayoutChanged] = useState(false); const resizableItemsList = configs.map(({ hidden, id, title }) => ({ id, label: title, @@ -389,12 +359,22 @@ const ResizableGridLayout = ({ } setCurrentBreakpoint(newBreakpoint); }} - onLayoutChange={(currentLayout, allLayouts) => { - if (!isLoaded || isLayoutConfigEqual(allLayouts, configs)) { + onDragStop={() => { + setHasLayoutChanged(true); + }} + onLayoutChange={(currentLayout: Layout[], allLayouts: Layouts) => { + if (!isLoaded) { return; } - const serializedLayouts = serializeLayoutsToConfig(allLayouts, configs); - onConfigUpdate(serializedLayouts); + + if (hasLayoutChanged) { + const serializedLayouts = serializeLayoutsToConfig(allLayouts, configs); + onConfigUpdate(serializedLayouts); + setHasLayoutChanged(false); + } + }} + onResizeStop={() => { + setHasLayoutChanged(true); }} rowHeight={98} width={size.width && size.width !== null ? size.width : 1280} @@ -408,6 +388,7 @@ const ResizableGridLayout = ({ const grid = layout[ currentBreakpoint as keyof IResizableGridLayoutConfig ] as ILayoutItemConfig; + return (
{layout.component} @@ -423,6 +404,7 @@ const ResizableGridLayout = ({ ); -}; +}, +arePropsEqual); export default ResizableGridLayout; diff --git a/packages/ui/src/layout/ResizableGridLayout/utils.ts b/packages/ui/src/layout/ResizableGridLayout/utils.ts index a641d34e6..012b794b0 100644 --- a/packages/ui/src/layout/ResizableGridLayout/utils.ts +++ b/packages/ui/src/layout/ResizableGridLayout/utils.ts @@ -38,13 +38,13 @@ export const observedPhenotypeDefaultGridConfig = { y: 0, }, xs: { - h: 4, + h: 6, w: 6, x: 0, y: 0, }, xxs: { - h: 4, + h: 6, w: 4, x: 0, y: 0, @@ -78,16 +78,16 @@ export const mondoDefaultGridConfig = { y: 0, }, xs: { - h: 4, + h: 6, w: 6, x: 0, - y: 4, + y: 6, }, xxs: { - h: 4, + h: 6, w: 4, x: 0, - y: 4, + y: 6, }, }; @@ -122,13 +122,13 @@ export const demographicsDefaultGridConfig = { h: 2, w: 6, x: 0, - y: 8, + y: 12, }, xxs: { h: 2, w: 4, x: 0, - y: 8, + y: 12, }, }; @@ -163,13 +163,13 @@ export const ageAtDiagnosisDefaultGridConfig = { h: 2, w: 6, x: 0, - y: 10, + y: 14, }, xxs: { h: 2, w: 4, x: 0, - y: 10, + y: 14, }, }; @@ -204,13 +204,13 @@ export const dataCategoryDefaultGridConfig = { h: 2, w: 6, x: 0, - y: 12, + y: 16, }, xxs: { h: 2, w: 4, x: 0, - y: 12, + y: 16, }, }; @@ -245,13 +245,13 @@ export const studiesDefaultGridConfig = { h: 3, w: 6, x: 0, - y: 14, + y: 18, }, xxs: { h: 3, w: 4, x: 0, - y: 14, + y: 18, }, }; @@ -286,12 +286,12 @@ export const dataTypeDefaultGridConfig = { h: 4, w: 6, x: 0, - y: 17, + y: 21, }, xxs: { h: 4, w: 4, x: 0, - y: 17, + y: 21, }, };