From 180f00ab2eec1c0cdf80f61dfb42369e90093f90 Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Thu, 18 Jan 2024 14:18:01 -0600 Subject: [PATCH 01/10] Migrate victory-tooltip to typescript --- packages/victory-core/src/types/callbacks.ts | 13 +- .../src/victory-util/user-props.ts | 14 + .../src/{flyout.test.js => flyout.test.tsx} | 6 +- .../src/{flyout.js => flyout.tsx} | 128 ++++++--- packages/victory-tooltip/src/index.d.ts | 92 ------- packages/victory-tooltip/src/index.js | 2 - packages/victory-tooltip/src/index.ts | 2 + ...oltip.test.js => victory-tooltip.test.tsx} | 4 +- ...victory-tooltip.js => victory-tooltip.tsx} | 258 +++++++++--------- 9 files changed, 259 insertions(+), 260 deletions(-) rename packages/victory-tooltip/src/{flyout.test.js => flyout.test.tsx} (89%) rename packages/victory-tooltip/src/{flyout.js => flyout.tsx} (57%) delete mode 100644 packages/victory-tooltip/src/index.d.ts delete mode 100644 packages/victory-tooltip/src/index.js create mode 100644 packages/victory-tooltip/src/index.ts rename packages/victory-tooltip/src/{victory-tooltip.test.js => victory-tooltip.test.tsx} (95%) rename packages/victory-tooltip/src/{victory-tooltip.js => victory-tooltip.tsx} (76%) diff --git a/packages/victory-core/src/types/callbacks.ts b/packages/victory-core/src/types/callbacks.ts index d1aba3206..aceb973d3 100644 --- a/packages/victory-core/src/types/callbacks.ts +++ b/packages/victory-core/src/types/callbacks.ts @@ -1,4 +1,4 @@ -import { D3Scale, Datum, ID } from "./prop-types"; +import { D3Scale, Datum, ID, ScalePropType } from "./prop-types"; import { BlockProps, OrientationTypes } from "../victory-theme/types"; /** @@ -16,10 +16,13 @@ export interface CallbackArgs { index?: ID; x?: number; y?: number; - scale?: { - x?: D3Scale; - y?: D3Scale; - }; + scale?: + | ScalePropType + | D3Scale + | { + x?: ScalePropType | D3Scale; + y?: ScalePropType | D3Scale; + }; tick?: any; ticks?: any; text?: any; diff --git a/packages/victory-core/src/victory-util/user-props.ts b/packages/victory-core/src/victory-util/user-props.ts index 2e86ca01a..4bb860c9e 100644 --- a/packages/victory-core/src/victory-util/user-props.ts +++ b/packages/victory-core/src/victory-util/user-props.ts @@ -55,6 +55,20 @@ const testIfSafeProp = (key: string): key is SafeAttribute => { return false; }; +/** + * Asserts that value is not null or undefined, throwing an error if it is. + * @param value The value to assert + * @param message The error message to throw + */ +export function assert( + value: T, + message?: string, +): asserts value is NonNullable { + if (value === undefined || value === null) { + throw new Error(message); + } +} + /** * getSafeUserProps - function that takes in a props object and removes any * key-value entries that do not match filter strings in the USER_PROPS_SAFELIST diff --git a/packages/victory-tooltip/src/flyout.test.js b/packages/victory-tooltip/src/flyout.test.tsx similarity index 89% rename from packages/victory-tooltip/src/flyout.test.js rename to packages/victory-tooltip/src/flyout.test.tsx index a793efa5f..8364fd0f4 100644 --- a/packages/victory-tooltip/src/flyout.test.js +++ b/packages/victory-tooltip/src/flyout.test.tsx @@ -1,7 +1,9 @@ import React from "react"; -import { Flyout } from "victory-tooltip"; import { render } from "@testing-library/react"; +import { SVGWrapper } from "../../../test/helpers"; +import { Flyout } from "./flyout"; + describe("victory-primitives/flyout", () => { const baseProps = { x: 100, @@ -17,7 +19,7 @@ describe("victory-primitives/flyout", () => { describe("rendering", () => { it("renders a flyout path", () => { const { container } = render(, { - wrapper: "svg", + wrapper: SVGWrapper, }); const path = container.querySelector("path"); diff --git a/packages/victory-tooltip/src/flyout.js b/packages/victory-tooltip/src/flyout.tsx similarity index 57% rename from packages/victory-tooltip/src/flyout.js rename to packages/victory-tooltip/src/flyout.tsx index d323439c8..de627e3d8 100644 --- a/packages/victory-tooltip/src/flyout.js +++ b/packages/victory-tooltip/src/flyout.tsx @@ -1,17 +1,67 @@ -/* eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1, 2] }]*/ import React from "react"; -import PropTypes from "prop-types"; -import { Helpers, CommonProps, Path, UserProps } from "victory-core"; -import { isPlainObject, assign } from "lodash"; +import { + Helpers, + Path, + UserProps, + VictoryCommonProps, + OrientationTypes, + VictoryStyleObject, + StringOrNumberOrCallback, +} from "victory-core"; -const getVerticalPath = (props) => { +export interface FlyoutProps extends VictoryCommonProps { + active?: boolean; + center?: { + x?: number; + y?: number; + }; + className?: string; + clipPath?: string; + cornerRadius?: number; + data?: any[]; + datum?: object; + dx?: number; + dy?: number; + events?: object; + id?: StringOrNumberOrCallback; + index?: number; + orientation?: OrientationTypes; + pathComponent?: React.ReactElement; + pointerLength?: number; + pointerWidth?: number; + role?: string; + shapeRendering?: string; + style?: VictoryStyleObject; + transform?: string; + x?: number; + y?: number; +} + +interface FlyoutPathProps { + center: { + x: number; + y: number; + }; + cornerRadius: number; + dx?: number; + dy?: number; + height: number; + orientation: OrientationTypes; + pointerLength: number; + pointerWidth: number; + width: number; + x: number; + y: number; +} + +const getVerticalPath = (props: FlyoutPathProps) => { const { pointerWidth, cornerRadius, orientation, width, height, center } = props; const sign = orientation === "bottom" ? 1 : -1; const x = props.x + (props.dx || 0); const y = props.y + (props.dy || 0); - const centerX = isPlainObject(center) && center.x; - const centerY = isPlainObject(center) && center.y; + const centerX = center.x; + const centerY = center.y; const pointerEdge = centerY + sign * (height / 2); const oppositeEdge = centerY - sign * (height / 2); const rightEdge = centerX + width / 2; @@ -35,14 +85,14 @@ const getVerticalPath = (props) => { z`; }; -const getHorizontalPath = (props) => { +const getHorizontalPath = (props: FlyoutPathProps) => { const { pointerWidth, cornerRadius, orientation, width, height, center } = props; const sign = orientation === "left" ? 1 : -1; const x = props.x + (props.dx || 0); const y = props.y + (props.dy || 0); - const centerX = isPlainObject(center) && center.x; - const centerY = isPlainObject(center) && center.y; + const centerX = center.x; + const centerY = center.y; const pointerEdge = centerX - sign * (width / 2); const oppositeEdge = centerX + sign * (width / 2); const bottomEdge = centerY + height / 2; @@ -66,14 +116,14 @@ const getHorizontalPath = (props) => { z`; }; -const getFlyoutPath = (props) => { +const getFlyoutPath = (props: FlyoutPathProps) => { const orientation = props.orientation || "top"; return orientation === "left" || orientation === "right" ? getHorizontalPath(props) : getVerticalPath(props); }; -const evaluateProps = (props) => { +const evaluateProps = (props: FlyoutProps) => { /** * Potential evaluated props are: * `id` @@ -82,7 +132,7 @@ const evaluateProps = (props) => { const id = Helpers.evaluateProp(props.id, props); const style = Helpers.evaluateStyle(props.style, props); - return assign({}, props, { id, style }); + return { ...props, id, style }; }; const defaultProps = { @@ -91,15 +141,42 @@ const defaultProps = { shapeRendering: "auto", }; -const Flyout = (initialProps) => { +export const Flyout = (initialProps: FlyoutProps) => { const props = evaluateProps({ ...defaultProps, ...initialProps }); const userProps = UserProps.getSafeUserProps(props); - return React.cloneElement(props.pathComponent, { + // check for required props for this subcomponent + // they should be passed in from the wrapper + UserProps.assert(props.center, "Flyout props[center] is undefined"); + UserProps.assert(props.height, "Flyout props[height] is undefined"); + UserProps.assert(props.width, "Flyout props[width] is undefined"); + UserProps.assert(props.x, "Flyout props[x] is undefined"); + UserProps.assert(props.y, "Flyout props[y] is undefined"); + + const { x: centerX, y: centerY } = props.center; + + UserProps.assert(centerX, "Flyout props[center.x] is undefined"); + UserProps.assert(centerY, "Flyout props[center.y] is undefined"); + + const flyoutPathProps: FlyoutPathProps = { + center: { x: centerX, y: centerY }, + cornerRadius: props.cornerRadius || 0, + dx: props.dx, + dy: props.dy, + height: props.height, + orientation: props.orientation || "top", + pointerLength: props.pointerLength || 0, + pointerWidth: props.pointerWidth || 0, + width: props.width, + x: props.x, + y: props.y, + }; + + return React.cloneElement(props.pathComponent!, { ...props.events, ...userProps, style: props.style, - d: getFlyoutPath(props), + d: getFlyoutPath(flyoutPathProps), className: props.className, shapeRendering: props.shapeRendering, role: props.role, @@ -107,22 +184,3 @@ const Flyout = (initialProps) => { clipPath: props.clipPath, }); }; - -Flyout.propTypes = { - ...CommonProps.primitiveProps, - center: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }), - cornerRadius: PropTypes.number, - datum: PropTypes.object, - dx: PropTypes.number, - dy: PropTypes.number, - height: PropTypes.number, - orientation: PropTypes.oneOf(["top", "bottom", "left", "right"]), - pathComponent: PropTypes.element, - pointerLength: PropTypes.number, - pointerWidth: PropTypes.number, - width: PropTypes.number, - x: PropTypes.number, - y: PropTypes.number, -}; - -export default Flyout; diff --git a/packages/victory-tooltip/src/index.d.ts b/packages/victory-tooltip/src/index.d.ts deleted file mode 100644 index 56a92f1e6..000000000 --- a/packages/victory-tooltip/src/index.d.ts +++ /dev/null @@ -1,92 +0,0 @@ -import * as React from "react"; -import { - OrientationTypes, - NumberOrCallback, - StringOrNumberOrCallback, - VictoryCommonProps, - VictoryLabelableProps, - VictoryThemeDefinition, - VictoryStyleObject, - VictoryLabelStyleObject, - PaddingOrCallback, - EventPropTypeInterface, -} from "victory-core"; - -export interface VictoryTooltipProps extends VictoryLabelableProps { - active?: boolean; - activateData?: boolean; - activePoints?: any[]; - angle?: string | number; - center?: { x: number | undefined; y: number | undefined }; - centerOffset?: { - x?: NumberOrCallback; - y?: NumberOrCallback; - }; - constrainToVisibleArea?: boolean; - cornerRadius?: NumberOrCallback; - datum?: {}; - data?: any[]; - dx?: NumberOrCallback; - dy?: NumberOrCallback; - groupComponent?: React.ReactElement; - height?: number; - horizontal?: boolean; - events?: { [key: string]: (event: React.SyntheticEvent) => void }; - flyoutHeight?: NumberOrCallback; - flyoutWidth?: NumberOrCallback; - flyoutStyle?: VictoryStyleObject; - flyoutComponent?: React.ReactElement; - flyoutPadding?: PaddingOrCallback; - index?: number | string; - orientation?: OrientationTypes | ((...args: any[]) => OrientationTypes); - pointerLength?: NumberOrCallback; - pointerOrientation?: - | OrientationTypes - | ((...args: any[]) => OrientationTypes); - pointerWidth?: NumberOrCallback; - renderInPortal?: boolean; - style?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; - text?: StringOrNumberOrCallback | string[] | number[]; - theme?: VictoryThemeDefinition; - width?: number; - x?: number; - y?: number; -} - -export interface FlyoutProps extends VictoryCommonProps { - active?: boolean; - center?: { - x?: number; - y?: number; - }; - className?: string; - cornerRadius?: number; - data?: any[]; - datum?: object; - dx?: number; - dy?: number; - events?: object; - height?: number; - id?: string | number; - index?: number; - orientation?: OrientationTypes; - pathComponent?: React.ReactElement; - pointerLength?: number; - pointerWidth?: number; - role?: string; - shapeRendering?: string; - style?: VictoryStyleObject; - transform?: string; - width?: number; - x?: number; - y?: number; -} - -export class Flyout extends React.Component {} - -export class VictoryTooltip extends React.Component { - static defaultEvents: EventPropTypeInterface< - string, - StringOrNumberOrCallback - >[]; -} diff --git a/packages/victory-tooltip/src/index.js b/packages/victory-tooltip/src/index.js deleted file mode 100644 index 3e290c3e2..000000000 --- a/packages/victory-tooltip/src/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as VictoryTooltip } from "./victory-tooltip"; -export { default as Flyout } from "./flyout"; diff --git a/packages/victory-tooltip/src/index.ts b/packages/victory-tooltip/src/index.ts new file mode 100644 index 000000000..c5e666721 --- /dev/null +++ b/packages/victory-tooltip/src/index.ts @@ -0,0 +1,2 @@ +export * from "./victory-tooltip"; +export * from "./flyout"; diff --git a/packages/victory-tooltip/src/victory-tooltip.test.js b/packages/victory-tooltip/src/victory-tooltip.test.tsx similarity index 95% rename from packages/victory-tooltip/src/victory-tooltip.test.js rename to packages/victory-tooltip/src/victory-tooltip.test.tsx index cb6c918d0..c7713b8fe 100644 --- a/packages/victory-tooltip/src/victory-tooltip.test.js +++ b/packages/victory-tooltip/src/victory-tooltip.test.tsx @@ -1,8 +1,10 @@ import React from "react"; -import { Flyout, VictoryTooltip } from "victory-tooltip"; import { VictoryContainer, VictoryLabel } from "victory-core"; import { fireEvent, screen, render } from "@testing-library/react"; +import { Flyout } from "./flyout"; +import { VictoryTooltip } from "./victory-tooltip"; + describe("components/victory-tooltip", () => { const flyoutId = "flyout-1"; const labelId = "label-1"; diff --git a/packages/victory-tooltip/src/victory-tooltip.js b/packages/victory-tooltip/src/victory-tooltip.tsx similarity index 76% rename from packages/victory-tooltip/src/victory-tooltip.js rename to packages/victory-tooltip/src/victory-tooltip.tsx index 0335422b2..6038b1e99 100644 --- a/packages/victory-tooltip/src/victory-tooltip.js +++ b/packages/victory-tooltip/src/victory-tooltip.tsx @@ -1,109 +1,95 @@ +/* eslint-disable react/prop-types */ import React from "react"; -import PropTypes from "prop-types"; import { - PropTypes as CustomPropTypes, TextSize, Helpers, LabelHelpers, VictoryLabel, VictoryTheme, VictoryPortal, + VictoryLabelableProps, + VictoryLabelProps, + VictoryLabelStyleObject, + NumberOrCallback, + PaddingOrCallback, + VictoryStyleObject, + OrientationTypes, + VictoryThemeDefinition, } from "victory-core"; -import Flyout from "./flyout"; import { assign, defaults, uniqueId, isPlainObject, orderBy } from "lodash"; +import { Flyout } from "./flyout"; + const fallbackProps = { cornerRadius: 5, pointerLength: 10, pointerWidth: 10, }; -export default class VictoryTooltip extends React.Component { +export interface VictoryTooltipProps + extends VictoryLabelableProps, + VictoryLabelProps { + activateData?: boolean; + active?: boolean; + activePoints?: any[]; + angle?: number; + center?: { x?: number; y?: number }; + centerOffset?: { + x?: NumberOrCallback; + y?: NumberOrCallback; + }; + constrainToVisibleArea?: boolean; + cornerRadius?: NumberOrCallback; + events?: any; + height?: number; + horizontal?: boolean; + flyoutComponent?: React.ReactElement; + flyoutHeight?: NumberOrCallback; + flyoutPadding?: PaddingOrCallback; + flyoutStyle?: VictoryStyleObject; + flyoutWidth?: NumberOrCallback; + id?: number | string; + index?: number | string; + orientation?: OrientationTypes | ((...args: any[]) => OrientationTypes); + pointerLength?: NumberOrCallback; + pointerOrientation?: + | OrientationTypes + | ((...args: any[]) => OrientationTypes); + pointerWidth?: NumberOrCallback; + style?: + | (VictoryLabelStyleObject & { + angle?: number; + }) + | VictoryLabelStyleObject[]; + theme?: VictoryThemeDefinition; + width?: number; +} + +type InternalEvaluatedProps = VictoryTooltipProps & { + centerOffset: { x: number; y: number }; + cornerRadius?: number; + dx?: string | number; + dy?: string | number; + flyoutHeight: number; + flyoutPadding: { + top: number; + bottom: number; + left: number; + right: number; + }; + flyoutWidth: number; + orientation: OrientationTypes; + pointerLength?: number; + pointerWidth?: number; + // TODO: This is a hack to get around the fact that the type of + // style is used in akward ways in the functions + style: any; + text: string | string[]; +}; + +export class VictoryTooltip extends React.Component { static displayName = "VictoryTooltip"; static role = "tooltip"; - static propTypes = { - activateData: PropTypes.bool, - active: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]), - activePoints: PropTypes.array, - angle: PropTypes.number, - center: PropTypes.shape({ - x: CustomPropTypes.nonNegative, - y: CustomPropTypes.nonNegative, - }), - centerOffset: PropTypes.shape({ - x: PropTypes.oneOfType([PropTypes.number, PropTypes.func]), - y: PropTypes.oneOfType([PropTypes.number, PropTypes.func]), - }), - constrainToVisibleArea: PropTypes.bool, - cornerRadius: PropTypes.oneOfType([ - CustomPropTypes.nonNegative, - PropTypes.func, - ]), - data: PropTypes.array, - datum: PropTypes.object, - dx: PropTypes.oneOfType([PropTypes.number, PropTypes.func]), - dy: PropTypes.oneOfType([PropTypes.number, PropTypes.func]), - events: PropTypes.object, - flyoutComponent: PropTypes.element, - flyoutHeight: PropTypes.oneOfType([ - CustomPropTypes.nonNegative, - PropTypes.func, - ]), - flyoutPadding: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.number, - PropTypes.shape({ - top: PropTypes.number, - bottom: PropTypes.number, - left: PropTypes.number, - right: PropTypes.number, - }), - ]), - flyoutStyle: PropTypes.object, - flyoutWidth: PropTypes.oneOfType([ - CustomPropTypes.nonNegative, - PropTypes.func, - ]), - groupComponent: PropTypes.element, - height: PropTypes.number, - horizontal: PropTypes.bool, - id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - index: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - labelComponent: PropTypes.element, - orientation: PropTypes.oneOfType([ - PropTypes.oneOf(["top", "bottom", "left", "right"]), - PropTypes.func, - ]), - pointerLength: PropTypes.oneOfType([ - CustomPropTypes.nonNegative, - PropTypes.func, - ]), - pointerOrientation: PropTypes.oneOfType([ - PropTypes.oneOf(["top", "bottom", "left", "right"]), - PropTypes.func, - ]), - pointerWidth: PropTypes.oneOfType([ - CustomPropTypes.nonNegative, - PropTypes.func, - ]), - polar: PropTypes.bool, - renderInPortal: PropTypes.bool, - scale: PropTypes.shape({ - x: CustomPropTypes.scale, - y: CustomPropTypes.scale, - }), - style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), - text: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number, - PropTypes.func, - PropTypes.array, - ]), - theme: PropTypes.object, - width: PropTypes.number, - x: PropTypes.number, - y: PropTypes.number, - }; static defaultProps = { active: false, @@ -113,7 +99,7 @@ export default class VictoryTooltip extends React.Component { groupComponent: , }; - static defaultEvents = (props) => { + static defaultEvents = (props: VictoryTooltipProps) => { const activate = props.activateData ? [ { target: "labels", mutation: () => ({ active: true }) }, @@ -141,25 +127,27 @@ export default class VictoryTooltip extends React.Component { ]; }; - constructor(props) { + id: string | number; + + constructor(props: VictoryTooltipProps) { super(props); this.id = props.id === undefined ? uniqueId("tooltip-") : props.id; } - getDefaultOrientation(props) { + getDefaultOrientation(props: VictoryTooltipProps): OrientationTypes { const { datum, horizontal, polar } = props; if (!polar) { const positive = horizontal ? "right" : "top"; const negative = horizontal ? "left" : "bottom"; return datum && datum.y < 0 ? negative : positive; } - return this.getPolarOrientation(props, datum); + return this.getPolarOrientation(props); } - getPolarOrientation(props, datum) { - const degrees = LabelHelpers.getDegrees(props, datum); + getPolarOrientation(props: VictoryTooltipProps): OrientationTypes { + const degrees = LabelHelpers.getDegrees(props, props.datum); const placement = props.labelPlacement || "vertical"; - if (placement === " vertical") { + if (placement === "vertical") { return this.getVerticalOrientations(degrees); } else if (placement === "parallel") { return degrees < 90 || degrees > 270 ? "right" : "left"; @@ -167,7 +155,7 @@ export default class VictoryTooltip extends React.Component { return degrees > 180 ? "bottom" : "top"; } - getVerticalOrientations(degrees) { + getVerticalOrientations(degrees: number): OrientationTypes { // eslint-disable-next-line no-magic-numbers if (degrees < 45 || degrees > 315) { return "right"; @@ -205,14 +193,19 @@ export default class VictoryTooltip extends React.Component { return { style, flyoutStyle }; } - getEvaluatedProps(props) { + getEvaluatedProps(props: VictoryTooltipProps): InternalEvaluatedProps { const { cornerRadius, centerOffset, dx, dy } = props; const active = Helpers.evaluateProp(props.active, props); - const text = Helpers.evaluateProp( - props.text, - assign({}, props, { active }), - ); + + let text = Helpers.evaluateProp(props.text, assign({}, props, { active })); + if (text === undefined || text === null) { + text = ""; + } + if (typeof text === "number") { + text = text.toString(); + } + const { style, flyoutStyle } = this.getStyles( assign({}, props, { active, text }), ); @@ -269,24 +262,32 @@ export default class VictoryTooltip extends React.Component { }); const offsetX = - isPlainObject(centerOffset) && centerOffset.x !== undefined + isPlainObject(centerOffset) && centerOffset?.x !== undefined ? Helpers.evaluateProp(centerOffset.x, evaluatedProps) : 0; const offsetY = - isPlainObject(centerOffset) && centerOffset.y !== undefined + isPlainObject(centerOffset) && centerOffset?.y !== undefined ? Helpers.evaluateProp(centerOffset.y, evaluatedProps) : 0; - return assign({}, evaluatedProps, { + return { + ...evaluatedProps, centerOffset: { x: offsetX, y: offsetY }, dx: dx !== undefined ? Helpers.evaluateProp(dx, evaluatedProps) : 0, dy: dy !== undefined ? Helpers.evaluateProp(dy, evaluatedProps) : 0, cornerRadius: Helpers.evaluateProp(cornerRadius, evaluatedProps), - }); + }; } - getCalculatedValues(props) { + getCalculatedValues(props: InternalEvaluatedProps): { + style: any; + flyoutStyle?: VictoryStyleObject; + labelSize: { width: number; height: number }; + flyoutDimensions: { height: number; width: number }; + flyoutCenter: { x: number; y: number }; + transform?: string; + } { const { style, text, flyoutStyle, flyoutHeight, flyoutWidth } = props; const labelSize = TextSize.approximateTextSize(text, style); const flyoutDimensions = { height: flyoutHeight, width: flyoutWidth }; @@ -302,7 +303,7 @@ export default class VictoryTooltip extends React.Component { }; } - getTransform(props) { + getTransform(props): string | undefined { const { x, y, style } = props; const labelStyle = style || {}; const angle = @@ -310,8 +311,7 @@ export default class VictoryTooltip extends React.Component { return angle ? `rotate(${angle} ${x} ${y})` : undefined; } - // eslint-disable-next-line complexity - getDefaultAngle(props) { + getDefaultAngle(props): number { const { polar, labelPlacement, orientation, datum } = props; if (!polar || !labelPlacement || labelPlacement === "vertical") { return 0; @@ -319,7 +319,8 @@ export default class VictoryTooltip extends React.Component { const degrees = LabelHelpers.getDegrees(props, datum); const sign = (degrees > 90 && degrees < 180) || degrees > 270 ? 1 : -1; const labelRotation = labelPlacement === "perpendicular" ? 0 : 90; - let angle; + + let angle = 0; if (degrees === 0 || degrees === 180) { angle = orientation === "top" && degrees === 180 ? 270 : 90; } else if (degrees > 0 && degrees < 180) { @@ -424,6 +425,7 @@ export default class VictoryTooltip extends React.Component { flyoutPadding, } = props; const cornerRadius = Helpers.evaluateProp(props.cornerRadius, props); + const getHeight = () => { const calculatedHeight = labelSize.height + flyoutPadding.top + flyoutPadding.bottom; @@ -434,6 +436,7 @@ export default class VictoryTooltip extends React.Component { : 2 * cornerRadius + pointerWidth; return Math.max(minHeight, calculatedHeight); }; + const getWidth = () => { const calculatedWidth = labelSize.width + flyoutPadding.left + flyoutPadding.right; @@ -444,17 +447,18 @@ export default class VictoryTooltip extends React.Component { : 2 * cornerRadius; return Math.max(minWidth, calculatedWidth); }; + return { flyoutHeight: flyoutHeight ? Helpers.evaluateProp(flyoutHeight, props) - : getHeight(props, labelSize, orientation), + : getHeight(), flyoutWidth: flyoutWidth ? Helpers.evaluateProp(flyoutWidth, props) - : getWidth(props, labelSize, orientation), + : getWidth(), }; } - getLabelProps(props, calculatedValues) { + getLabelProps(props: InternalEvaluatedProps, calculatedValues) { const { flyoutCenter, style, labelSize, dy = 0, dx = 0 } = calculatedValues; const { text, datum, activePoints, labelComponent, index, flyoutPadding } = props; @@ -469,7 +473,7 @@ export default class VictoryTooltip extends React.Component { const sign = textAnchor === "end" ? -1 : 1; return flyoutCenter.x - sign * (labelSize.width / 2); }; - return defaults({}, labelComponent.props, { + return defaults({}, labelComponent!.props, { key: `${this.id}-label-${index}`, text, datum, @@ -485,7 +489,11 @@ export default class VictoryTooltip extends React.Component { }); } - getPointerOrientation(point, center, flyoutDimensions) { + getPointerOrientation( + point: { x: number; y: number }, + center: { x: number; y: number }, + flyoutDimensions: { height: number; width: number }, + ): string { const edges = { bottom: center.y + flyoutDimensions.height / 2, top: center.y - flyoutDimensions.height / 2, @@ -509,7 +517,7 @@ export default class VictoryTooltip extends React.Component { return orderBy(gaps, "val", "desc")[0].side; } - getFlyoutProps(props, calculatedValues) { + getFlyoutProps(props: InternalEvaluatedProps, calculatedValues) { const { flyoutDimensions, flyoutStyle, flyoutCenter } = calculatedValues; const { x, @@ -529,7 +537,7 @@ export default class VictoryTooltip extends React.Component { props.pointerOrientation, props, ); - return defaults({}, flyoutComponent.props, { + return defaults({}, flyoutComponent!.props, { x, y, dx, @@ -543,7 +551,11 @@ export default class VictoryTooltip extends React.Component { events, orientation: pointerOrientation || - this.getPointerOrientation({ x, y }, flyoutCenter, flyoutDimensions), + this.getPointerOrientation( + { x: x!, y: y! }, + flyoutCenter, + flyoutDimensions, + ), key: `${this.id}-tooltip-${index}`, width: flyoutDimensions.width, height: flyoutDimensions.height, @@ -553,34 +565,34 @@ export default class VictoryTooltip extends React.Component { } // Overridden in victory-core-native - renderTooltip(props) { + renderTooltip(props: VictoryTooltipProps): React.ReactElement | null { const active = Helpers.evaluateProp(props.active, props); const { renderInPortal } = props; if (!active) { - return renderInPortal ? {null} : null; + return renderInPortal ? : null; } const evaluatedProps = this.getEvaluatedProps(props); const { flyoutComponent, labelComponent, groupComponent } = evaluatedProps; const calculatedValues = this.getCalculatedValues(evaluatedProps); const children = [ React.cloneElement( - flyoutComponent, + flyoutComponent!, this.getFlyoutProps(evaluatedProps, calculatedValues), ), React.cloneElement( - labelComponent, + labelComponent!, this.getLabelProps(evaluatedProps, calculatedValues), ), ]; const tooltip = React.cloneElement( - groupComponent, + groupComponent!, { role: "presentation", transform: calculatedValues.transform }, children, ); return renderInPortal ? {tooltip} : tooltip; } - render() { + render(): React.ReactElement | null { const props = Helpers.modifyProps(this.props, fallbackProps, "tooltip"); return this.renderTooltip(props); } From f929e163c50685afb8f7f2091b47833d7cbeeea0 Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Thu, 18 Jan 2024 14:36:33 -0600 Subject: [PATCH 02/10] Add self reference for tests --- packages/victory-tooltip/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/victory-tooltip/package.json b/packages/victory-tooltip/package.json index 55310c9e5..fb27de47d 100644 --- a/packages/victory-tooltip/package.json +++ b/packages/victory-tooltip/package.json @@ -24,6 +24,9 @@ "prop-types": "^15.8.1", "victory-core": "^36.8.2" }, + "devDependencies": { + "victory-tooltip": "*" + }, "peerDependencies": { "react": ">=16.6.0" }, From bf6b803ad63c8c0024e798e7af70ceead93b30e6 Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Thu, 18 Jan 2024 14:38:49 -0600 Subject: [PATCH 03/10] Update lockfile --- pnpm-lock.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fcde3ca40..6e7b10054 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -693,10 +693,13 @@ importers: lodash: ^4.17.19 prop-types: ^15.8.1 victory-core: ^36.8.2 + victory-tooltip: '*' dependencies: lodash: 4.17.21 prop-types: 15.8.1 victory-core: link:../victory-core + devDependencies: + victory-tooltip: 'link:' packages/victory-vendor: specifiers: From ece6c2da746b76a49ab7dadedb6442dfe2a234db Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Thu, 18 Jan 2024 14:57:53 -0600 Subject: [PATCH 04/10] Type flyout functional component --- packages/victory-tooltip/src/flyout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/victory-tooltip/src/flyout.tsx b/packages/victory-tooltip/src/flyout.tsx index de627e3d8..fd05034fe 100644 --- a/packages/victory-tooltip/src/flyout.tsx +++ b/packages/victory-tooltip/src/flyout.tsx @@ -141,7 +141,7 @@ const defaultProps = { shapeRendering: "auto", }; -export const Flyout = (initialProps: FlyoutProps) => { +export const Flyout: React.FC = (initialProps) => { const props = evaluateProps({ ...defaultProps, ...initialProps }); const userProps = UserProps.getSafeUserProps(props); From ec6aa8e157751488f8e3bd5a568eaf1adf6e12b1 Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Fri, 19 Jan 2024 05:46:20 -0600 Subject: [PATCH 05/10] Fix tests --- packages/victory-core/src/exports.test.ts | 1 + packages/victory-tooltip/src/flyout.test.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/victory-core/src/exports.test.ts b/packages/victory-core/src/exports.test.ts index ffe4a9d47..5a237528f 100644 --- a/packages/victory-core/src/exports.test.ts +++ b/packages/victory-core/src/exports.test.ts @@ -408,6 +408,7 @@ describe("victory-core", () => { "getTransitionPropsFactory": [Function], }, "UserProps": Object { + "assert": [Function], "getSafeUserProps": [Function], "withSafeUserProps": [Function], }, diff --git a/packages/victory-tooltip/src/flyout.test.tsx b/packages/victory-tooltip/src/flyout.test.tsx index 8364fd0f4..811d1a07f 100644 --- a/packages/victory-tooltip/src/flyout.test.tsx +++ b/packages/victory-tooltip/src/flyout.test.tsx @@ -1,8 +1,8 @@ import React from "react"; import { render } from "@testing-library/react"; +import { Flyout } from "victory-tooltip"; import { SVGWrapper } from "../../../test/helpers"; -import { Flyout } from "./flyout"; describe("victory-primitives/flyout", () => { const baseProps = { From 0878d3f784a44b0f57522502224cb0531d324c48 Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Fri, 19 Jan 2024 05:57:03 -0600 Subject: [PATCH 06/10] Use remote ref --- packages/victory-tooltip/src/victory-tooltip.test.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/victory-tooltip/src/victory-tooltip.test.tsx b/packages/victory-tooltip/src/victory-tooltip.test.tsx index c7713b8fe..2804c4177 100644 --- a/packages/victory-tooltip/src/victory-tooltip.test.tsx +++ b/packages/victory-tooltip/src/victory-tooltip.test.tsx @@ -1,15 +1,14 @@ import React from "react"; -import { VictoryContainer, VictoryLabel } from "victory-core"; import { fireEvent, screen, render } from "@testing-library/react"; +import { VictoryContainer, VictoryLabel } from "victory-core"; +import { VictoryTooltip } from "victory-tooltip"; import { Flyout } from "./flyout"; -import { VictoryTooltip } from "./victory-tooltip"; describe("components/victory-tooltip", () => { const flyoutId = "flyout-1"; const labelId = "label-1"; - /** @type {VictoryTooltipProps} */ const baseProps = { x: 0, y: 0, From 75778e07a13e1200929eaee651628861612f393a Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Fri, 19 Jan 2024 05:59:08 -0600 Subject: [PATCH 07/10] Wip --- packages/victory-tooltip/src/victory-tooltip.test.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/victory-tooltip/src/victory-tooltip.test.tsx b/packages/victory-tooltip/src/victory-tooltip.test.tsx index 2804c4177..322a9e8a1 100644 --- a/packages/victory-tooltip/src/victory-tooltip.test.tsx +++ b/packages/victory-tooltip/src/victory-tooltip.test.tsx @@ -1,9 +1,7 @@ import React from "react"; import { fireEvent, screen, render } from "@testing-library/react"; import { VictoryContainer, VictoryLabel } from "victory-core"; -import { VictoryTooltip } from "victory-tooltip"; - -import { Flyout } from "./flyout"; +import { Flyout, VictoryTooltip } from "victory-tooltip"; describe("components/victory-tooltip", () => { const flyoutId = "flyout-1"; From 291fed4e95c03c83d548a0ba3a3f60e456c04f05 Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Fri, 19 Jan 2024 06:04:23 -0600 Subject: [PATCH 08/10] Test center --- packages/victory-tooltip/src/flyout.test.tsx | 1 + packages/victory-tooltip/src/flyout.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/victory-tooltip/src/flyout.test.tsx b/packages/victory-tooltip/src/flyout.test.tsx index 811d1a07f..ff19af89a 100644 --- a/packages/victory-tooltip/src/flyout.test.tsx +++ b/packages/victory-tooltip/src/flyout.test.tsx @@ -15,6 +15,7 @@ describe("victory-primitives/flyout", () => { cornerRadius: 5, pointerLength: 10, pointerWidth: 10, + // center: { x: 100, y: 100 }, }; describe("rendering", () => { it("renders a flyout path", () => { diff --git a/packages/victory-tooltip/src/flyout.tsx b/packages/victory-tooltip/src/flyout.tsx index fd05034fe..64cb4198e 100644 --- a/packages/victory-tooltip/src/flyout.tsx +++ b/packages/victory-tooltip/src/flyout.tsx @@ -147,19 +147,19 @@ export const Flyout: React.FC = (initialProps) => { // check for required props for this subcomponent // they should be passed in from the wrapper - UserProps.assert(props.center, "Flyout props[center] is undefined"); + // UserProps.assert(props.center, "Flyout props[center] is undefined"); UserProps.assert(props.height, "Flyout props[height] is undefined"); UserProps.assert(props.width, "Flyout props[width] is undefined"); UserProps.assert(props.x, "Flyout props[x] is undefined"); UserProps.assert(props.y, "Flyout props[y] is undefined"); - const { x: centerX, y: centerY } = props.center; + // const { x: centerX, y: centerY } = props.center; - UserProps.assert(centerX, "Flyout props[center.x] is undefined"); - UserProps.assert(centerY, "Flyout props[center.y] is undefined"); + // UserProps.assert(centerX, "Flyout props[center.x] is undefined"); + // UserProps.assert(centerY, "Flyout props[center.y] is undefined"); const flyoutPathProps: FlyoutPathProps = { - center: { x: centerX, y: centerY }, + center: props.center, cornerRadius: props.cornerRadius || 0, dx: props.dx, dy: props.dy, From 8538a5d5ef88343dcf15b716e74c2b7ed13ccf5c Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Fri, 19 Jan 2024 06:14:07 -0600 Subject: [PATCH 09/10] Allow optional center --- packages/victory-tooltip/src/flyout.test.tsx | 1 - packages/victory-tooltip/src/flyout.tsx | 12 +++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/victory-tooltip/src/flyout.test.tsx b/packages/victory-tooltip/src/flyout.test.tsx index ff19af89a..811d1a07f 100644 --- a/packages/victory-tooltip/src/flyout.test.tsx +++ b/packages/victory-tooltip/src/flyout.test.tsx @@ -15,7 +15,6 @@ describe("victory-primitives/flyout", () => { cornerRadius: 5, pointerLength: 10, pointerWidth: 10, - // center: { x: 100, y: 100 }, }; describe("rendering", () => { it("renders a flyout path", () => { diff --git a/packages/victory-tooltip/src/flyout.tsx b/packages/victory-tooltip/src/flyout.tsx index 64cb4198e..bc1615307 100644 --- a/packages/victory-tooltip/src/flyout.tsx +++ b/packages/victory-tooltip/src/flyout.tsx @@ -12,8 +12,8 @@ import { export interface FlyoutProps extends VictoryCommonProps { active?: boolean; center?: { - x?: number; - y?: number; + x: number; + y: number; }; className?: string; clipPath?: string; @@ -147,19 +147,13 @@ export const Flyout: React.FC = (initialProps) => { // check for required props for this subcomponent // they should be passed in from the wrapper - // UserProps.assert(props.center, "Flyout props[center] is undefined"); UserProps.assert(props.height, "Flyout props[height] is undefined"); UserProps.assert(props.width, "Flyout props[width] is undefined"); UserProps.assert(props.x, "Flyout props[x] is undefined"); UserProps.assert(props.y, "Flyout props[y] is undefined"); - // const { x: centerX, y: centerY } = props.center; - - // UserProps.assert(centerX, "Flyout props[center.x] is undefined"); - // UserProps.assert(centerY, "Flyout props[center.y] is undefined"); - const flyoutPathProps: FlyoutPathProps = { - center: props.center, + center: props.center || { x: 0, y: 0 }, cornerRadius: props.cornerRadius || 0, dx: props.dx, dy: props.dy, From aca0bb78d0a70ed668386c9fb81ffb674ad63335 Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Fri, 19 Jan 2024 06:23:26 -0600 Subject: [PATCH 10/10] Changeset --- .changeset/tender-bees-talk.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/tender-bees-talk.md diff --git a/.changeset/tender-bees-talk.md b/.changeset/tender-bees-talk.md new file mode 100644 index 000000000..ba72ebc5f --- /dev/null +++ b/.changeset/tender-bees-talk.md @@ -0,0 +1,6 @@ +--- +"victory-core": patch +"victory-tooltip": patch +--- + +Migrate victory-tooltip to typescript