diff --git a/superset-frontend/packages/superset-ui-core/src/chart-composition/ChartFrame.tsx b/superset-frontend/packages/superset-ui-core/src/chart-composition/ChartFrame.tsx index 88cf52ec72f..91836814e9f 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart-composition/ChartFrame.tsx +++ b/superset-frontend/packages/superset-ui-core/src/chart-composition/ChartFrame.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { PureComponent, ReactNode } from 'react'; +import { memo, ReactNode } from 'react'; import { isDefined } from '../utils'; @@ -29,7 +29,7 @@ type Props = { contentWidth?: number; contentHeight?: number; height: number; - renderContent: ({ + renderContent?: ({ height, width, }: { @@ -39,36 +39,35 @@ type Props = { width: number; }; -export default class ChartFrame extends PureComponent { - static defaultProps = { - renderContent() {}, - }; +function ChartFrame({ + contentWidth, + contentHeight, + width, + height, + renderContent = () => null, +}: Props) { + const overflowX = checkNumber(contentWidth) && contentWidth > width; + const overflowY = checkNumber(contentHeight) && contentHeight > height; - render() { - const { contentWidth, contentHeight, width, height, renderContent } = - this.props; - - const overflowX = checkNumber(contentWidth) && contentWidth > width; - const overflowY = checkNumber(contentHeight) && contentHeight > height; - - if (overflowX || overflowY) { - return ( -
- {renderContent({ - height: Math.max(contentHeight ?? 0, height), - width: Math.max(contentWidth ?? 0, width), - })} -
- ); - } - - return renderContent({ height, width }); + if (overflowX || overflowY) { + return ( +
+ {renderContent({ + height: Math.max(contentHeight ?? 0, height), + width: Math.max(contentWidth ?? 0, width), + })} +
+ ); } + + return <>{renderContent({ height, width })}; } + +export default memo(ChartFrame); diff --git a/superset-frontend/packages/superset-ui-core/src/chart-composition/legend/WithLegend.tsx b/superset-frontend/packages/superset-ui-core/src/chart-composition/legend/WithLegend.tsx index a2cac92ee33..48e7309cdab 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart-composition/legend/WithLegend.tsx +++ b/superset-frontend/packages/superset-ui-core/src/chart-composition/legend/WithLegend.tsx @@ -17,26 +17,19 @@ * under the License. */ -import { CSSProperties, ReactNode, PureComponent } from 'react'; +import { CSSProperties, ReactNode, memo, useMemo } from 'react'; import { ParentSize } from '@visx/responsive'; -const defaultProps = { - className: '', - height: 'auto' as number | string, - position: 'top', - width: 'auto' as number | string, -}; - type Props = { - className: string; + className?: string; debounceTime?: number; - width: number | string; - height: number | string; + width?: number | string; + height?: number | string; legendJustifyContent?: 'center' | 'flex-start' | 'flex-end'; - position: 'top' | 'left' | 'bottom' | 'right'; + position?: 'top' | 'left' | 'bottom' | 'right'; renderChart: (dim: { width: number; height: number }) => ReactNode; renderLegend?: (params: { direction: string }) => ReactNode; -} & Readonly; +}; const LEGEND_STYLE_BASE: CSSProperties = { display: 'flex', @@ -52,95 +45,101 @@ const CHART_STYLE_BASE: CSSProperties = { position: 'relative', }; -class WithLegend extends PureComponent { - static defaultProps = defaultProps; - - getContainerDirection(): CSSProperties['flexDirection'] { - const { position } = this.props; - - if (position === 'left') { - return 'row'; - } - if (position === 'right') { - return 'row-reverse'; - } - if (position === 'bottom') { - return 'column-reverse'; - } - - return 'column'; +function getContainerDirection( + position: Props['position'], +): CSSProperties['flexDirection'] { + if (position === 'left') { + return 'row'; + } + if (position === 'right') { + return 'row-reverse'; + } + if (position === 'bottom') { + return 'column-reverse'; } - getLegendJustifyContent() { - const { legendJustifyContent, position } = this.props; - if (legendJustifyContent) { - return legendJustifyContent; - } - - if (position === 'left' || position === 'right') { - return 'flex-start'; - } - - return 'flex-end'; - } - - render() { - const { - className, - debounceTime, - width, - height, - position, - renderChart, - renderLegend, - } = this.props; - - const isHorizontal = position === 'left' || position === 'right'; - - const style: CSSProperties = { - display: 'flex', - flexDirection: this.getContainerDirection(), - height, - width, - }; - - const chartStyle: CSSProperties = { ...CHART_STYLE_BASE }; - if (isHorizontal) { - chartStyle.width = 0; - } else { - chartStyle.height = 0; - } - - const legendDirection = isHorizontal ? 'column' : 'row'; - const legendStyle: CSSProperties = { - ...LEGEND_STYLE_BASE, - flexDirection: legendDirection, - justifyContent: this.getLegendJustifyContent(), - }; - - return ( -
- {renderLegend && ( -
- {renderLegend({ - // Pass flexDirection for @vx/legend to arrange legend items - direction: legendDirection, - })} -
- )} -
- - {(parent: { width: number; height: number }) => - parent.width > 0 && parent.height > 0 - ? // Only render when necessary - renderChart(parent) - : null - } - -
-
- ); - } + return 'column'; } -export default WithLegend; +function getLegendJustifyContent( + legendJustifyContent: Props['legendJustifyContent'], + position: Props['position'], +) { + if (legendJustifyContent) { + return legendJustifyContent; + } + + if (position === 'left' || position === 'right') { + return 'flex-start'; + } + + return 'flex-end'; +} + +function WithLegend({ + className = '', + debounceTime, + width = 'auto', + height = 'auto', + legendJustifyContent, + position = 'top', + renderChart, + renderLegend, +}: Props) { + const isHorizontal = position === 'left' || position === 'right'; + + const style: CSSProperties = useMemo( + () => ({ + display: 'flex', + flexDirection: getContainerDirection(position), + height, + width, + }), + [position, height, width], + ); + + const chartStyle: CSSProperties = useMemo(() => { + const baseStyle = { ...CHART_STYLE_BASE }; + if (isHorizontal) { + baseStyle.width = 0; + } else { + baseStyle.height = 0; + } + return baseStyle; + }, [isHorizontal]); + + const legendDirection = isHorizontal ? 'column' : 'row'; + const legendStyle: CSSProperties = useMemo( + () => ({ + ...LEGEND_STYLE_BASE, + flexDirection: legendDirection, + justifyContent: getLegendJustifyContent(legendJustifyContent, position), + }), + [legendDirection, legendJustifyContent, position], + ); + + return ( +
+ {renderLegend && ( +
+ {renderLegend({ + // Pass flexDirection for @vx/legend to arrange legend items + direction: legendDirection, + })} +
+ )} +
+ + {(parent: { width: number; height: number }) => + parent.width > 0 && parent.height > 0 + ? // Only render when necessary + renderChart(parent) + : null + } + +
+
+ ); +} + +export default memo(WithLegend); diff --git a/superset-frontend/packages/superset-ui-core/src/chart-composition/tooltip/TooltipFrame.tsx b/superset-frontend/packages/superset-ui-core/src/chart-composition/tooltip/TooltipFrame.tsx index d470a229403..19008654316 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart-composition/tooltip/TooltipFrame.tsx +++ b/superset-frontend/packages/superset-ui-core/src/chart-composition/tooltip/TooltipFrame.tsx @@ -17,31 +17,21 @@ * under the License. */ -import { PureComponent, ReactNode } from 'react'; - -const defaultProps = { - className: '', -}; +import { memo, ReactNode } from 'react'; type Props = { className?: string; children: ReactNode; -} & Readonly; +}; const CONTAINER_STYLE = { padding: 8 }; -class TooltipFrame extends PureComponent { - static defaultProps = defaultProps; - - render() { - const { className, children } = this.props; - - return ( -
- {children} -
- ); - } +function TooltipFrame({ className = '', children }: Props) { + return ( +
+ {children} +
+ ); } -export default TooltipFrame; +export default memo(TooltipFrame);