diff --git a/.husky/pre-commit b/.husky/pre-commit index 3e1cef884..f27575a8e 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npm run precommit diff --git a/package-lock.json b/package-lock.json index 715407e92..1d21909d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,7 +61,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-storybook": "^0.6.15", "html-webpack-plugin": "^5.5.0", - "husky": "^8.0.0", + "husky": "^9.0.10", "lint-staged": "^13.2.2", "postcss-styled-syntax": "^0.5.0", "prettier": "^2.8.3", @@ -12312,15 +12312,15 @@ } }, "node_modules/husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.10.tgz", + "integrity": "sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==", "dev": true, "bin": { - "husky": "lib/bin.js" + "husky": "bin.mjs" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/typicode" @@ -27839,9 +27839,9 @@ "dev": true }, "husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.10.tgz", + "integrity": "sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==", "dev": true }, "iconv-lite": { diff --git a/package.json b/package.json index 06e34c7ed..4b5547961 100644 --- a/package.json +++ b/package.json @@ -33,10 +33,11 @@ "build:troubleshooting:prod": "webpack --config webpack.prod.ts --env app=troubleshooting", "build:prod": "webpack --config webpack.prod.ts", "build:prod:web": "webpack --config webpack.prod.ts --env platform=Web", - "precommit": "lint-staged" + "precommit": "lint-staged", + "prepare": "husky" }, "lint-staged": { - "*.{js,ts}": [ + "*.{js,ts,tsx}": [ "eslint --cache" ], "src/**/*.ts": [ @@ -83,7 +84,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-storybook": "^0.6.15", "html-webpack-plugin": "^5.5.0", - "husky": "^8.0.0", + "husky": "^9.0.10", "lint-staged": "^13.2.2", "postcss-styled-syntax": "^0.5.0", "prettier": "^2.8.3", diff --git a/src/components/Assets/AssetsFilter/index.tsx b/src/components/Assets/AssetsFilter/index.tsx index 49eee102d..c8f419d44 100644 --- a/src/components/Assets/AssetsFilter/index.tsx +++ b/src/components/Assets/AssetsFilter/index.tsx @@ -28,6 +28,8 @@ import { AssetsFiltersData } from "./types"; +const PERSISTENCE_KEY = "assetsFilters"; + const REFRESH_INTERVAL = isNumber(window.assetsRefreshInterval) ? window.assetsRefreshInterval : 10 * 1000; // in milliseconds @@ -68,7 +70,7 @@ export const AssetsFilter = (props: AssetsFilterProps) => { const [isOpen, setIsOpen] = useState(false); const previousIsOpen = usePrevious(isOpen); const [persistedFilters, setPersistedFilters] = - usePersistence("assetsFilters", "project"); + usePersistence(PERSISTENCE_KEY, "project"); const previousPersistedFilters = usePrevious(persistedFilters); const [selectedServices, setSelectedServices] = useState([]); const [selectedEndpoints, setSelectedEndpoints] = useState([]); diff --git a/src/components/Insights/BottleneckInsight/index.tsx b/src/components/Insights/BottleneckInsight/index.tsx index aa845cbcc..521b3f523 100644 --- a/src/components/Insights/BottleneckInsight/index.tsx +++ b/src/components/Insights/BottleneckInsight/index.tsx @@ -73,6 +73,7 @@ export const BottleneckInsight = (props: BottleneckInsightProps) => { spanCodeObjectId={props.insight.spanInfo?.spanCodeObjectId} ticketLink={props.insight.ticketLink} buttonType={"large"} + isHintEnabled={props.isJiraHintEnabled} /> diff --git a/src/components/Insights/EndpointNPlusOneInsight/index.tsx b/src/components/Insights/EndpointNPlusOneInsight/index.tsx index 8e74712c9..a17276c44 100644 --- a/src/components/Insights/EndpointNPlusOneInsight/index.tsx +++ b/src/components/Insights/EndpointNPlusOneInsight/index.tsx @@ -105,6 +105,7 @@ export const EndpointNPlusOneInsight = ( spanCodeObjectId={spanInfo.spanCodeObjectId} ticketLink={span.ticketLink} buttonType={"small"} + isHintEnabled={props.isJiraHintEnabled} /> {config.isJaegerEnabled && ( diff --git a/src/components/Insights/HighNumberOfQueriesInsight/index.tsx b/src/components/Insights/HighNumberOfQueriesInsight/index.tsx index bd7813e19..e9f4d6631 100644 --- a/src/components/Insights/HighNumberOfQueriesInsight/index.tsx +++ b/src/components/Insights/HighNumberOfQueriesInsight/index.tsx @@ -77,6 +77,7 @@ export const HighNumberOfQueriesInsight = ( spanCodeObjectId={insight.spanInfo?.spanCodeObjectId} ticketLink={insight.ticketLink} buttonType={"small"} + isHintEnabled={props.isJiraHintEnabled} /> {traceId && ( diff --git a/src/components/Insights/InsightJiraTicket/index.tsx b/src/components/Insights/InsightJiraTicket/index.tsx index e45001d74..131a2c4bc 100644 --- a/src/components/Insights/InsightJiraTicket/index.tsx +++ b/src/components/Insights/InsightJiraTicket/index.tsx @@ -15,9 +15,8 @@ export const InsightJiraTicket = (props: InsightJiraTicketProps) => { ); const config = useContext(ConfigContext); - const isLinkUnlinkInputVisible = getFeatureFlagValue( - config, - FeatureFlag.IS_INSIGHT_TICKET_LINKAGE_ENABLED + const isLinkUnlinkInputVisible = Boolean( + getFeatureFlagValue(config, FeatureFlag.IS_INSIGHT_TICKET_LINKAGE_ENABLED) ); const linkTicket = (link: string) => { @@ -95,7 +94,7 @@ export const InsightJiraTicket = (props: InsightJiraTicketProps) => { ticketLink={{ link: ticketLink, errorMessage }} unlinkTicket={unlinkTicket} linkTicket={linkTicket} - showLinkButton={!!isLinkUnlinkInputVisible} + showLinkButton={isLinkUnlinkInputVisible} /> ); }; diff --git a/src/components/Insights/InsightList/index.tsx b/src/components/Insights/InsightList/index.tsx index 15373876d..e599f64b5 100644 --- a/src/components/Insights/InsightList/index.tsx +++ b/src/components/Insights/InsightList/index.tsx @@ -1,6 +1,8 @@ import { useEffect, useState } from "react"; import { DefaultTheme, useTheme } from "styled-components"; +import { usePersistence } from "../../../hooks/usePersistence"; import { trackingEvents as globalTrackingEvents } from "../../../trackingEvents"; +import { isUndefined } from "../../../typeGuards/isUndefined"; import { InsightType } from "../../../types"; import { getInsightTypeInfo } from "../../../utils/getInsightTypeInfo"; import { sendTrackingEvent } from "../../../utils/sendTrackingEvent"; @@ -68,7 +70,7 @@ import { Trace } from "../types"; import * as s from "./styles"; -import { InsightListProps } from "./types"; +import { InsightListProps, isInsightJiraTicketHintShownPayload } from "./types"; export const getInsightTypeOrderPriority = (type: string): number => { const insightOrderPriorityMap: Record = { @@ -103,6 +105,39 @@ export const getInsightTypeOrderPriority = (type: string): number => { return insightOrderPriorityMap[type] || -Infinity; }; +const getInsightToShowJiraHint = ( + insightGroups: InsightGroup[] +): { groupIndex: number; insightIndex: number } | null => { + const insightsWithJiraButton = [ + InsightType.EndpointSpanNPlusOne, + InsightType.SpanNPlusOne, + InsightType.SpanEndpointBottleneck, + InsightType.SlowestSpans, + InsightType.SpanQueryOptimization, + InsightType.EndpointHighNumberOfQueries + ]; + + let insightIndex = -1; + const insightsWithJiraButtonIndex = insightGroups.findIndex((x) => + x.insights.some((insight, i) => { + if (insightsWithJiraButton.includes(insight.type)) { + insightIndex = i; + return true; + } + return false; + }) + ); + + if ([insightsWithJiraButtonIndex, insightIndex].includes(-1)) { + return null; + } + + return { + groupIndex: insightsWithJiraButtonIndex, + insightIndex: insightIndex + }; +}; + const groupInsights = ( insights: GenericCodeObjectInsight[], spans: MethodSpan[] @@ -222,7 +257,8 @@ const renderInsightCard = ( onJiraTicketCreate: ( insight: GenericCodeObjectInsight, spanCodeObjectId?: string - ) => void + ) => void, + isJiraHintEnabled: boolean ): JSX.Element | undefined => { const handleErrorSelect = (errorId: string, insightType: InsightType) => { sendTrackingEvent(globalTrackingEvents.USER_ACTION, { @@ -378,6 +414,7 @@ const renderInsightCard = ( onRecalculate={handleRecalculate} onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} + isJiraHintEnabled={isJiraHintEnabled} /> ); } @@ -390,6 +427,7 @@ const renderInsightCard = ( onRecalculate={handleRecalculate} onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} + isJiraHintEnabled={isJiraHintEnabled} /> ); } @@ -439,6 +477,7 @@ const renderInsightCard = ( onRecalculate={handleRecalculate} onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} + isJiraHintEnabled={isJiraHintEnabled} /> ); } @@ -452,6 +491,7 @@ const renderInsightCard = ( onRecalculate={handleRecalculate} onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} + isJiraHintEnabled={isJiraHintEnabled} /> ); } @@ -565,6 +605,7 @@ const renderInsightCard = ( onRecalculate={handleRecalculate} onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} + isJiraHintEnabled={isJiraHintEnabled} /> ); } @@ -590,16 +631,27 @@ const renderInsightCard = ( onRecalculate={handleRecalculate} onRefresh={handleRefresh} onJiraTicketCreate={onJiraTicketCreate} + isJiraHintEnabled={isJiraHintEnabled} /> ); } }; +const IS_INSIGHT_JIRA_TICKET_HINT_SHOWN_PERSISTENCE_KEY = + "isInsightJiraTicketHintShown"; + export const InsightList = (props: InsightListProps) => { const [insightGroups, setInsightGroups] = useState([]); const [isAutofixing, setIsAutofixing] = useState(false); const theme = useTheme(); const insightGroupIconColor = getInsightGroupIconColor(theme); + const [isInsightJiraTicketHintShown, setIsInsightJiraTicketHintShown] = + usePersistence( + IS_INSIGHT_JIRA_TICKET_HINT_SHOWN_PERSISTENCE_KEY, + "application" + ); + + const insightWithJiraHint = getInsightToShowJiraHint(insightGroups); useEffect(() => { window.scrollTo(0, 0); @@ -640,9 +692,17 @@ export const InsightList = (props: InsightListProps) => { setIsAutofixing(true); }; + const handleShowJiraTicket = ( + insight: GenericCodeObjectInsight, + spanCodeObjectId?: string + ) => { + props.onJiraTicketCreate(insight, spanCodeObjectId); + setIsInsightJiraTicketHintShown({ value: true }); + }; + return ( - {insightGroups.map((x) => ( + {insightGroups.map((x, i) => ( {x.name && ( @@ -655,9 +715,19 @@ export const InsightList = (props: InsightListProps) => { )} {x.insights.length > 0 ? ( - x.insights.map((insight) => - renderInsightCard(insight, props.onJiraTicketCreate) - ) + x.insights.map((insight, j) => { + const isJiraHintEnabled = + !isUndefined(isInsightJiraTicketHintShown) && + !isInsightJiraTicketHintShown?.value && + i === insightWithJiraHint?.groupIndex && + j === insightWithJiraHint?.insightIndex; + + return renderInsightCard( + insight, + handleShowJiraTicket, + isJiraHintEnabled + ); + }) ) : ( No data yet} diff --git a/src/components/Insights/InsightList/types.ts b/src/components/Insights/InsightList/types.ts index e16848d44..5c53dd29e 100644 --- a/src/components/Insights/InsightList/types.ts +++ b/src/components/Insights/InsightList/types.ts @@ -14,3 +14,7 @@ export interface InsightListProps { spanCodeObjectId?: string ) => void; } + +export interface isInsightJiraTicketHintShownPayload { + value: boolean; +} diff --git a/src/components/Insights/NPlusOneInsight/index.tsx b/src/components/Insights/NPlusOneInsight/index.tsx index e84833084..ae5b1ce28 100644 --- a/src/components/Insights/NPlusOneInsight/index.tsx +++ b/src/components/Insights/NPlusOneInsight/index.tsx @@ -138,6 +138,7 @@ export const NPlusOneInsight = (props: NPlusOneInsightProps) => { spanCodeObjectId={props.insight.spanInfo?.spanCodeObjectId} ticketLink={props.insight.ticketLink} buttonType={"large"} + isHintEnabled={props.isJiraHintEnabled} /> ]} /> diff --git a/src/components/Insights/QueryOptimizationInsight/index.tsx b/src/components/Insights/QueryOptimizationInsight/index.tsx index ecef00493..f8542b2f0 100644 --- a/src/components/Insights/QueryOptimizationInsight/index.tsx +++ b/src/components/Insights/QueryOptimizationInsight/index.tsx @@ -108,6 +108,7 @@ export const QueryOptimizationInsight = ( spanCodeObjectId={props.insight.spanInfo?.spanCodeObjectId} ticketLink={props.insight.ticketLink} buttonType={"large"} + isHintEnabled={props.isJiraHintEnabled} /> ]} /> diff --git a/src/components/Insights/SpanBottleneckInsight/index.tsx b/src/components/Insights/SpanBottleneckInsight/index.tsx index fceadc3f0..0719f7967 100644 --- a/src/components/Insights/SpanBottleneckInsight/index.tsx +++ b/src/components/Insights/SpanBottleneckInsight/index.tsx @@ -66,6 +66,7 @@ export const SpanBottleneckInsight = (props: SpanBottleneckInsightProps) => { spanCodeObjectId={spanCodeObjectId} ticketLink={span.ticketLink} buttonType={"small"} + isHintEnabled={props.isJiraHintEnabled} /> diff --git a/src/components/Insights/common/JiraButton/index.tsx b/src/components/Insights/common/JiraButton/index.tsx index a655d72a1..c97bb6845 100644 --- a/src/components/Insights/common/JiraButton/index.tsx +++ b/src/components/Insights/common/JiraButton/index.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { useTheme } from "styled-components"; import { openURLInDefaultBrowser } from "../../../../utils/openURLInDefaultBrowser"; import { Button } from "../../../common/Button"; import { Menu } from "../../../common/Menu"; @@ -14,29 +15,26 @@ import { JiraButtonProps } from "./types"; export const JiraButton = (props: JiraButtonProps) => { const [isJiraPopoverOpen, setIsJiraPopoverOpen] = useState(false); + const theme = useTheme(); const handleJiraButtonClick = () => { setIsJiraPopoverOpen(!isJiraPopoverOpen); }; + const handleViewButtonClick = () => { + props.ticketLink && openURLInDefaultBrowser(props.ticketLink); + }; + + const openTicketInfo = () => { + props.onTicketInfoButtonClick(props.spanCodeObjectId); + }; + const menuWidth = props.buttonType == "large" ? "119px" : "70px"; const buttonText = props.buttonType == "large" ? "Ticket Info" : ""; - return ( - <> - {!props.ticketLink && ( - - - - )} - {props.ticketLink && ( + const renderButton = () => ( +
+ {props.ticketLink ? ( { icon: { component: OpenLinkIcon }, label: "View", value: props.ticketLink, - onClick: () => - props.ticketLink && - openURLInDefaultBrowser(props.ticketLink) + onClick: handleViewButtonClick }, { icon: { component: PencilIcon }, label: "Edit", value: props.spanCodeObjectId ?? "", - onClick: () => - props.onTicketInfoButtonClick(props.spanCodeObjectId) + onClick: openTicketInfo } ]} onSelect={handleJiraButtonClick} @@ -78,7 +73,49 @@ export const JiraButton = (props: JiraButtonProps) => { {buttonText} + ) : ( + + + )} - +
+ ); + + return ( + + + + + + Get Ticket Info + + + You can now easily create a ticket using information from Digma + + + + } + isOpen={props.isHintEnabled} + > + {renderButton()} + ); }; diff --git a/src/components/Insights/common/JiraButton/styles.ts b/src/components/Insights/common/JiraButton/styles.ts index 7e255a34c..607d142ea 100644 --- a/src/components/Insights/common/JiraButton/styles.ts +++ b/src/components/Insights/common/JiraButton/styles.ts @@ -1,8 +1,37 @@ import styled from "styled-components"; import { Button } from "../../../common/Button"; +import { NewButton } from "../../../common/NewButton"; export const StyledButton = styled(Button)` display: flex; flex-direction: column; gap: 6px; `; + +export const HintContainer = styled.div` + display: flex; + flex-direction: column; + gap: 4px; + padding: 4px; + width: 235px; + color: ${({ theme }) => theme.colors.text.subtext}; + word-break: keep-all; +`; + +export const HintHeader = styled.div` + display: flex; + gap: 4px; + align-items: center; + font-weight: 500; + color: ${({ theme }) => theme.colors.text.base}; +`; + +export const HintIconContainer = styled.div` + display: flex; + color: ${({ theme }) => theme.colors.icon.primary}; +`; + +export const TryNowButton = styled(NewButton)` + margin-top: 8px; + align-self: flex-end; +`; diff --git a/src/components/Insights/common/JiraButton/types.ts b/src/components/Insights/common/JiraButton/types.ts index 233e3223d..0131366ac 100644 --- a/src/components/Insights/common/JiraButton/types.ts +++ b/src/components/Insights/common/JiraButton/types.ts @@ -3,4 +3,5 @@ export interface JiraButtonProps { ticketLink?: string | null; spanCodeObjectId?: string; buttonType: "small" | "large"; + isHintEnabled?: boolean; } diff --git a/src/components/Insights/types.ts b/src/components/Insights/types.ts index cfef94472..cc0049419 100644 --- a/src/components/Insights/types.ts +++ b/src/components/Insights/types.ts @@ -86,6 +86,7 @@ export interface InsightProps { insight: GenericCodeObjectInsight, spanCodeObjectId?: string ) => void; + isJiraHintEnabled?: boolean; } export interface InsightTicketInfo { diff --git a/src/components/RecentActivity/EnvironmentTypePanel/EnvironmentTypePanel.stories.tsx b/src/components/RecentActivity/EnvironmentTypePanel/EnvironmentTypePanel.stories.tsx index f114b8ebb..2034b74a3 100644 --- a/src/components/RecentActivity/EnvironmentTypePanel/EnvironmentTypePanel.stories.tsx +++ b/src/components/RecentActivity/EnvironmentTypePanel/EnvironmentTypePanel.stories.tsx @@ -16,4 +16,18 @@ export default meta; type Story = StoryObj; // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args -export const Default: Story = {}; +export const Default: Story = { + args: { + environment: { + originalName: "environmentName", + hasRecentActivity: false, + name: "environmentName", + isPending: true, + additionToConfigResult: null, + type: null, + token: null, + serverApiUrl: null, + isOrgDigmaSetupFinished: false + } + } +}; diff --git a/src/components/Tests/TestTicket/index.tsx b/src/components/Tests/TestTicket/index.tsx index a088a025f..bc96156e1 100644 --- a/src/components/Tests/TestTicket/index.tsx +++ b/src/components/Tests/TestTicket/index.tsx @@ -35,8 +35,8 @@ export const TestTicket = (props: TestTicketProps) => { : "" }`} , -
Last run at: ${new Date(runAt).toString()}
, -
Duration: ${getDurationString(duration)}
, +
Last run at: {new Date(runAt).toString()}
, +
Duration: {getDurationString(duration)}
, <> {relatedSpans.length > 0 && (
{`Related spans:\n${relatedSpans}`}
diff --git a/src/components/common/Card/styles.tsx b/src/components/common/Card/styles.ts similarity index 100% rename from src/components/common/Card/styles.tsx rename to src/components/common/Card/styles.ts diff --git a/src/components/common/IconTag/styles.ts b/src/components/common/IconTag/styles.ts index bd0f98675..3128268ca 100644 --- a/src/components/common/IconTag/styles.ts +++ b/src/components/common/IconTag/styles.ts @@ -1,7 +1,7 @@ import styled from "styled-components"; import { ContainerProps, IconTagSize } from "./types"; -const getDimensions = (size: IconTagSize) => (size === "large" ? 28 : 20); //in pixels +const getDimensions = (size: IconTagSize) => (size === "large" ? 28 : 20); // in pixels export const Container = styled.div` display: flex; diff --git a/src/components/common/JiraTicket/Field/styles.ts b/src/components/common/JiraTicket/Field/styles.ts index 81c93784a..eef93e676 100644 --- a/src/components/common/JiraTicket/Field/styles.ts +++ b/src/components/common/JiraTicket/Field/styles.ts @@ -6,7 +6,7 @@ export const Container = styled.div` display: flex; flex-direction: column; gap: 6px; - ${({ $selectable }) => (!$selectable ? "user-select: none;" : "")} + ${({ $selectable }) => ($selectable ? "" : "user-select: none;")} `; export const Label = styled.label` diff --git a/src/components/common/JiraTicket/TicketLinkButton/index.tsx b/src/components/common/JiraTicket/TicketLinkButton/index.tsx index b720e1ba7..c88d533d4 100644 --- a/src/components/common/JiraTicket/TicketLinkButton/index.tsx +++ b/src/components/common/JiraTicket/TicketLinkButton/index.tsx @@ -53,7 +53,7 @@ export const TicketLinkButton = (props: TicketLinkButtonProps) => { } label={"Ticket URL"} onChange={onTicketLinkChange} - disabled={!!props.ticketLink?.link} + disabled={Boolean(props.ticketLink?.link)} errorMessage={errorMessage} buttons={ props.ticketLink?.link ? ( @@ -64,7 +64,7 @@ export const TicketLinkButton = (props: TicketLinkButtonProps) => { diff --git a/src/components/common/Tooltip/index.tsx b/src/components/common/Tooltip/index.tsx index e9c83fcb0..b13052c88 100644 --- a/src/components/common/Tooltip/index.tsx +++ b/src/components/common/Tooltip/index.tsx @@ -3,6 +3,7 @@ import { FloatingPortal, Placement, arrow, + autoUpdate, flip, offset, shift, @@ -12,6 +13,8 @@ import { } from "@floating-ui/react"; import { Children, cloneElement, useRef, useState } from "react"; import { useTheme } from "styled-components"; +import { isBoolean } from "../../../typeGuards/isBoolean"; +import { isString } from "../../../typeGuards/isString"; import * as s from "./styles"; import { TooltipProps } from "./types"; @@ -59,9 +62,10 @@ export const Tooltip = (props: TooltipProps) => { const placement = props.placement || "top"; const { refs, floatingStyles, context } = useFloating({ + whileElementsMounted: autoUpdate, placement, - open: isOpen, - onOpenChange: setIsOpen, + open: isBoolean(props.isOpen) ? props.isOpen : isOpen, + onOpenChange: isBoolean(props.isOpen) ? undefined : setIsOpen, middleware: [ offset(ARROW_HEIGHT + GAP), flip(), @@ -72,7 +76,10 @@ export const Tooltip = (props: TooltipProps) => { ] }); - const hover = useHover(context, { delay: { open: 1000, close: 0 } }); + const hover = useHover(context, { + delay: { open: 1000, close: 0 }, + enabled: !isBoolean(props.isOpen) + }); const { getReferenceProps, getFloatingProps } = useInteractions([hover]); @@ -84,19 +91,24 @@ export const Tooltip = (props: TooltipProps) => { ...getReferenceProps() }) )} - {isOpen && ( + {(isBoolean(props.isOpen) ? props.isOpen : isOpen) && ( ( scope } }); + setValue(value); // TODO: handle error }, [key, scope] );