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 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-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/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" }, diff --git a/packages/victory-tooltip/src/flyout.test.js b/packages/victory-tooltip/src/flyout.test.tsx similarity index 92% rename from packages/victory-tooltip/src/flyout.test.js rename to packages/victory-tooltip/src/flyout.test.tsx index a793efa5f..811d1a07f 100644 --- a/packages/victory-tooltip/src/flyout.test.js +++ b/packages/victory-tooltip/src/flyout.test.tsx @@ -1,6 +1,8 @@ import React from "react"; -import { Flyout } from "victory-tooltip"; import { render } from "@testing-library/react"; +import { Flyout } from "victory-tooltip"; + +import { SVGWrapper } from "../../../test/helpers"; describe("victory-primitives/flyout", () => { const baseProps = { @@ -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 60% rename from packages/victory-tooltip/src/flyout.js rename to packages/victory-tooltip/src/flyout.tsx index d323439c8..bc1615307 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,36 @@ const defaultProps = { shapeRendering: "auto", }; -const Flyout = (initialProps) => { +export const Flyout: React.FC = (initialProps) => { 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.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 flyoutPathProps: FlyoutPathProps = { + center: props.center || { x: 0, y: 0 }, + 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 +178,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 98% rename from packages/victory-tooltip/src/victory-tooltip.test.js rename to packages/victory-tooltip/src/victory-tooltip.test.tsx index cb6c918d0..322a9e8a1 100644 --- a/packages/victory-tooltip/src/victory-tooltip.test.js +++ b/packages/victory-tooltip/src/victory-tooltip.test.tsx @@ -1,13 +1,12 @@ 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 { VictoryContainer, VictoryLabel } from "victory-core"; +import { Flyout, VictoryTooltip } from "victory-tooltip"; describe("components/victory-tooltip", () => { const flyoutId = "flyout-1"; const labelId = "label-1"; - /** @type {VictoryTooltipProps} */ const baseProps = { x: 0, y: 0, 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); } 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: