From a5d23948b104f4c1b2fbcad91f90646fd834d698 Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Mon, 13 Jul 2026 20:08:45 -0300 Subject: [PATCH 1/2] Migrate slide and annotation group Menus to the items API. Avoid the antd Menu children deprecation warning by building items in the list components instead of wrapping content in Menu.Item. --- src/components/AnnotationGroupItem.tsx | 51 +++++---------- src/components/AnnotationGroupList.tsx | 38 ++++++----- src/components/SlideItem.tsx | 91 ++++++++++++-------------- src/components/SlideList.tsx | 40 ++++------- 4 files changed, 92 insertions(+), 128 deletions(-) diff --git a/src/components/AnnotationGroupItem.tsx b/src/components/AnnotationGroupItem.tsx index cc80dfbb..caec9519 100644 --- a/src/components/AnnotationGroupItem.tsx +++ b/src/components/AnnotationGroupItem.tsx @@ -6,7 +6,6 @@ import { Col, Divider, InputNumber, - Menu, Popover, Row, Select, @@ -564,41 +563,25 @@ class AnnotationGroupItem extends React.Component< const color = this.getCurrentColor() const isBadgeVisible = this.state.isVisible && this.state.currentStyle.measurement === null - const { - annotationGroup, - defaultStyle, - isVisible, - metadata, - onVisibilityChange, - onStyleChange, - onAnnotationGroupClick, - ...otherProps - } = this.props return ( - - -
- -
- +
+ - - +
+ +
) } } diff --git a/src/components/AnnotationGroupList.tsx b/src/components/AnnotationGroupList.tsx index 05cf29c4..afcb1c3b 100644 --- a/src/components/AnnotationGroupList.tsx +++ b/src/components/AnnotationGroupList.tsx @@ -1,3 +1,4 @@ +import type { MenuProps } from 'antd' import { Menu, Switch } from 'antd' // skipcq: JS-C1003 import type * as dcmjs from 'dcmjs' @@ -67,21 +68,26 @@ class AnnotationGroupList extends React.Component< } render(): React.ReactNode { - const items = this.props.annotationGroups.map((annotationGroup, _index) => { - const uid = annotationGroup.uid - return ( - - ) - }) + const items: MenuProps['items'] = this.props.annotationGroups.map( + (annotationGroup) => { + const uid = annotationGroup.uid + return { + key: uid, + style: { height: '100%', paddingLeft: '3px' }, + label: ( + + ), + } + }, + ) return ( <> @@ -100,7 +106,7 @@ class AnnotationGroupList extends React.Component< unCheckedChildren={} /> - {items} + ) } diff --git a/src/components/SlideItem.tsx b/src/components/SlideItem.tsx index d79b870b..3d4fb91f 100644 --- a/src/components/SlideItem.tsx +++ b/src/components/SlideItem.tsx @@ -1,4 +1,4 @@ -import { Menu, Typography } from 'antd' +import { Typography } from 'antd' // skipcq: JS-C1003 import * as dmv from 'dicom-microscopy-viewer' import React from 'react' @@ -106,60 +106,51 @@ class SlideItem extends React.Component { return } - /* Properties need to be propagated down to Menu.Item: - * https://github.com/react-component/menu/issues/142 - */ return ( - - -
- {this.props.slide.overviewImages.length > 0 || - this.props.slide.thumbnailImages.length > 0 ? ( -
- ) : ( -
- SM -
- )} - -
- {this.props.slide.seriesDescription !== undefined && - this.props.slide.seriesDescription !== null && - this.props.slide.seriesDescription !== '' ? ( - + {this.props.slide.overviewImages.length > 0 || + this.props.slide.thumbnailImages.length > 0 ? ( +
+ ) : ( +
- {this.props.slide.seriesDescription} - - ) : null} - - + SM +
+ )} + +
+ {this.props.slide.seriesDescription !== undefined && + this.props.slide.seriesDescription !== null && + this.props.slide.seriesDescription !== '' ? ( + + {this.props.slide.seriesDescription} + + ) : null} + ) } } diff --git a/src/components/SlideList.tsx b/src/components/SlideList.tsx index e5e9601a..4c3aa482 100644 --- a/src/components/SlideList.tsx +++ b/src/components/SlideList.tsx @@ -1,3 +1,4 @@ +import type { MenuProps } from 'antd' import { Menu } from 'antd' import React from 'react' @@ -35,32 +36,16 @@ class SlideList extends React.Component { } render(): React.ReactNode { - const slideList = this.props.metadata - const slideItemList = [] - for (let i = 0; i < slideList.length; ++i) { - const slide = slideList[i] - const slideItem = ( - - ) - - slideItemList.push(slideItem) - } + const items: MenuProps['items'] = this.props.metadata.map((slide) => { + const key = slide.seriesInstanceUIDs[0] + return { + key, + style: { height: '100%' }, + label: , + } + }) - const handleMenuItemSelection = ({ - key, - keyPath: _keyPath, - domEvent: _domEvent, - selectedKeys: _selectedKeys, - }: { - key: React.ReactText - keyPath: React.ReactText[] - domEvent: React.MouseEvent | React.KeyboardEvent - selectedKeys?: React.ReactText[] - }): void => { + const handleMenuItemSelection: MenuProps['onSelect'] = ({ key }) => { console.info(`select slide "${key}"`) this.setState({ selectedSeriesInstanceUID: key.toString() }) this.props.onSeriesSelection({ seriesInstanceUID: key.toString() }) @@ -81,9 +66,8 @@ class SlideList extends React.Component { onSelect={handleMenuItemSelection} mode="inline" inlineIndent={0} - > - {slideItemList} -
+ items={items} + /> ) } } From fa7fa1e7e0e143d3206b7f8d758dcb708d6be92c Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Mon, 13 Jul 2026 20:10:40 -0300 Subject: [PATCH 2/2] Replace Header Badge counts to avoid findDOMNode warnings. antd Badge zoom motion uses rc-motion findDOMNode under React Strict Mode; use static count pills for the debug button and modal panels instead. --- src/components/Header.tsx | 131 +++++++++++++++++++++++++++++++++----- 1 file changed, 115 insertions(+), 16 deletions(-) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 2337f056..1bc9a225 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -9,7 +9,6 @@ import { UserOutlined, } from '@ant-design/icons' import { - Badge, Col, Collapse, Dropdown, @@ -86,6 +85,59 @@ const aboutModalStyles: Record = { }, } +/** + * Static count pill that avoids antd Badge → rc-motion `findDOMNode` + * (deprecated under React Strict Mode). + */ +function HeaderCountBadge({ + count, + color = '#ff4d4f', + zIndex, + children, +}: { + count: number + color?: string + zIndex?: number + children?: React.ReactNode +}): JSX.Element { + const pill = + count > 0 ? ( + + {count > 99 ? '99+' : count} + + ) : null + + if (children == null) { + return <>{pill} + } + + return ( + + {children} + {pill} + + ) +} + interface HeaderProps extends RouteComponentProps { app: { name: string @@ -421,11 +473,14 @@ class Header extends React.Component { const { Panel } = Collapse const showErrorCount = (errcount: number): JSX.Element => ( - + ) const showWarningCount = (warncount: number): JSX.Element => ( - 0 ? 'green' : undefined} count={warncount} /> + 0 ? '#52c41a' : '#ff4d4f'} + /> ) Modal.info({ @@ -612,19 +667,63 @@ class Header extends React.Component { ) const debugButton = ( - - 0 ? 'green' : undefined} - count={this.state.warnings.length} - style={{ zIndex: 1001 }} - > -