diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx
index b870435b7..a61b06a5e 100644
--- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx
+++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx
@@ -21,172 +21,192 @@ import AddMetricContainer from '../DashboardWidgetGrid/AddMetricContainer';
import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
interface IProps {
- siteId: string;
- dashboardId: any;
- renderReport?: any;
+ siteId: string;
+ dashboardId: any;
+ renderReport?: any;
}
type Props = IProps & RouteComponentProps;
function DashboardView(props: Props) {
- const { siteId, dashboardId } = props;
- const { dashboardStore } = useStore();
- const { showModal } = useModal();
+ const { siteId, dashboardId } = props;
+ const { dashboardStore } = useStore();
+ const { showModal } = useModal();
- const [showTooltip, setShowTooltip] = React.useState(false);
- const [focusTitle, setFocusedInput] = React.useState(true);
- const [showEditModal, setShowEditModal] = React.useState(false);
+ const [showTooltip, setShowTooltip] = React.useState(false);
+ const [focusTitle, setFocusedInput] = React.useState(true);
+ const [showEditModal, setShowEditModal] = React.useState(false);
- const showAlertModal = dashboardStore.showAlertModal;
- const loading = dashboardStore.fetchingDashboard;
- const dashboard: any = dashboardStore.selectedDashboard;
- const period = dashboardStore.period;
+ const showAlertModal = dashboardStore.showAlertModal;
+ const loading = dashboardStore.fetchingDashboard;
+ const dashboard: any = dashboardStore.selectedDashboard;
+ const period = dashboardStore.period;
- const queryParams = new URLSearchParams(props.location.search);
+ const queryParams = new URLSearchParams(props.location.search);
- const trimQuery = () => {
- if (!queryParams.has('modal')) return;
- queryParams.delete('modal');
- props.history.replace({
- search: queryParams.toString(),
- });
- };
- const pushQuery = () => {
- if (!queryParams.has('modal')) props.history.push('?modal=addMetric');
- };
+ const trimQuery = () => {
+ if (!queryParams.has('modal')) return;
+ queryParams.delete('modal');
+ props.history.replace({
+ search: queryParams.toString(),
+ });
+ };
+ const pushQuery = () => {
+ if (!queryParams.has('modal')) props.history.push('?modal=addMetric');
+ };
- useEffect(() => {
- if (queryParams.has('modal')) {
- onAddWidgets();
- trimQuery();
- }
- }, []);
+ useEffect(() => {
+ if (queryParams.has('modal')) {
+ onAddWidgets();
+ trimQuery();
+ }
+ }, []);
- useEffect(() => {
- const isExists = dashboardStore.getDashboardById(dashboardId);
- if (!isExists) {
- props.history.push(withSiteId(`/dashboard`, siteId));
- }
- }, [dashboardId]);
+ useEffect(() => {
+ const isExists = dashboardStore.getDashboardById(dashboardId);
+ if (!isExists) {
+ props.history.push(withSiteId(`/dashboard`, siteId));
+ }
+ }, [dashboardId]);
- useEffect(() => {
- if (!dashboard || !dashboard.dashboardId) return;
- dashboardStore.fetch(dashboard.dashboardId);
- }, [dashboard]);
+ useEffect(() => {
+ if (!dashboard || !dashboard.dashboardId) return;
+ dashboardStore.fetch(dashboard.dashboardId);
+ }, [dashboard]);
- const onAddWidgets = () => {
- dashboardStore.initDashboard(dashboard);
- showModal( , { right: true });
- };
-
- const onEdit = (isTitle: boolean) => {
- dashboardStore.initDashboard(dashboard);
- setFocusedInput(isTitle);
- setShowEditModal(true);
- };
-
- const onDelete = async () => {
- if (
- await confirm({
- header: 'Confirm',
- confirmButton: 'Yes, delete',
- confirmation: `Are you sure you want to permanently delete this Dashboard?`,
- })
- ) {
- dashboardStore.deleteDashboard(dashboard).then(() => {
- props.history.push(withSiteId(`/dashboard`, siteId));
- });
- }
- };
-
- if (!dashboard) return null;
-
- return (
-
-
-
setShowEditModal(false)} focusTitle={focusTitle} />
-
-
-
-
- {dashboard?.name}
-
- }
- onDoubleClick={() => onEdit(true)}
- className="mr-3 select-none border-b border-b-borderColor-transparent hover:border-dotted hover:border-gray-medium cursor-pointer"
- actionButton={
- /* @ts-ignore */
-
- setShowTooltip(false)}>
- setShowTooltip(false)} isPopup siteId={siteId} />
-
-
- }
- >
-
setShowTooltip(true)}>
- Add Metric
-
-
- }
- />
-
-
-
- dashboardStore.setPeriod(period)}
- right={true}
- />
-
-
-
-
-
-
-
-
- {/* @ts-ignore */}
-
- onEdit(false)}
- >
- {dashboard?.description || 'Describe the purpose of this dashboard'}
-
-
-
-
- dashboardStore.updateKey('showAlertModal', false)} />
-
-
+ const onAddWidgets = () => {
+ dashboardStore.initDashboard(dashboard);
+ showModal(
+ ,
+ { right: true }
);
+ };
+
+ const onEdit = (isTitle: boolean) => {
+ dashboardStore.initDashboard(dashboard);
+ setFocusedInput(isTitle);
+ setShowEditModal(true);
+ };
+
+ const onDelete = async () => {
+ if (
+ await confirm({
+ header: 'Confirm',
+ confirmButton: 'Yes, delete',
+ confirmation: `Are you sure you want to permanently delete this Dashboard?`,
+ })
+ ) {
+ dashboardStore.deleteDashboard(dashboard).then(() => {
+ props.history.push(withSiteId(`/dashboard`, siteId));
+ });
+ }
+ };
+
+ if (!dashboard) return null;
+
+ return (
+
+
+
setShowEditModal(false)}
+ focusTitle={focusTitle}
+ />
+
+
+
+
+ {dashboard?.name}
+
+ }
+ onDoubleClick={() => onEdit(true)}
+ className="mr-3 select-none border-b border-b-borderColor-transparent hover:border-dotted hover:border-gray-medium cursor-pointer"
+ actionButton={
+ setShowTooltip(false)}>
+
+ setShowTooltip(false)}
+ isPopup
+ siteId={siteId}
+ />
+
+ }
+ >
+
setShowTooltip(true)}>
+ Add Metric
+
+
+
+ }
+ />
+
+
+
+ dashboardStore.setPeriod(period)}
+ right={true}
+ />
+
+
+
+
+
+
+
+
+ {/* @ts-ignore */}
+
+ onEdit(false)}
+ >
+ {dashboard?.description || 'Describe the purpose of this dashboard'}
+
+
+
+
+ dashboardStore.updateKey('showAlertModal', false)}
+ />
+
+
+ );
}
// @ts-ignore
-export default withPageTitle('Dashboards - OpenReplay')(withReport(withRouter(withModal(observer(DashboardView)))));
+export default withPageTitle('Dashboards - OpenReplay')(
+ withReport(withRouter(withModal(observer(DashboardView))))
+);
diff --git a/frontend/app/components/Session_/Issues/Issues.js b/frontend/app/components/Session_/Issues/Issues.js
index 200b5b278..30be2e5c6 100644
--- a/frontend/app/components/Session_/Issues/Issues.js
+++ b/frontend/app/components/Session_/Issues/Issues.js
@@ -5,24 +5,27 @@ import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
import IssuesModal from './IssuesModal';
import { fetchProjects, fetchMeta } from 'Duck/assignments';
import stl from './issues.module.css';
-import { Tooltip } from 'react-tippy'
+import { Tooltip } from 'react-tippy';
-@connect(state => ({
- issues: state.getIn(['assignments', 'list']),
- metaLoading: state.getIn(['assignments', 'fetchMeta', 'loading']),
- projects: state.getIn(['assignments', 'projects']),
- projectsFetched: state.getIn(['assignments', 'projectsFetched']),
- activeIssue: state.getIn(['assignments', 'activeIssue']),
- fetchIssueLoading: state.getIn(['assignments', 'fetchAssignment', 'loading']),
- fetchIssuesLoading: state.getIn(['assignments', 'fetchAssignments', 'loading']),
- projectsLoading: state.getIn(['assignments', 'fetchProjects', 'loading']),
- issuesIntegration: state.getIn([ 'issues', 'list']).first() || {},
+@connect(
+ (state) => ({
+ issues: state.getIn(['assignments', 'list']),
+ metaLoading: state.getIn(['assignments', 'fetchMeta', 'loading']),
+ projects: state.getIn(['assignments', 'projects']),
+ projectsFetched: state.getIn(['assignments', 'projectsFetched']),
+ activeIssue: state.getIn(['assignments', 'activeIssue']),
+ fetchIssueLoading: state.getIn(['assignments', 'fetchAssignment', 'loading']),
+ fetchIssuesLoading: state.getIn(['assignments', 'fetchAssignments', 'loading']),
+ projectsLoading: state.getIn(['assignments', 'fetchProjects', 'loading']),
+ issuesIntegration: state.getIn(['issues', 'list']).first() || {},
- jiraConfig: state.getIn([ 'issues', 'list' ]).first(),
- issuesFetched: state.getIn([ 'issues', 'issuesFetched' ]),
-}), { fetchMeta, fetchProjects })
+ jiraConfig: state.getIn(['issues', 'list']).first(),
+ issuesFetched: state.getIn(['issues', 'issuesFetched']),
+ }),
+ { fetchMeta, fetchProjects }
+)
class Issues extends React.Component {
- state = {showModal: false };
+ state = { showModal: false };
constructor(props) {
super(props);
@@ -31,64 +34,79 @@ class Issues extends React.Component {
closeModal = () => {
this.setState({ showModal: false });
- }
+ };
showIssuesList = (e) => {
e.preventDefault();
e.stopPropagation();
this.setState({ showModal: true });
- }
+ };
handleOpen = () => {
this.setState({ showModal: true });
- if (!this.props.projectsFetched) { // cache projects fetch
- this.props.fetchProjects().then(function() {
- const { projects } = this.props;
- if (projects && projects.first()) {
- this.props.fetchMeta(projects.first().id)
- }
- }.bind(this))
+ if (!this.props.projectsFetched) {
+ // cache projects fetch
+ this.props.fetchProjects().then(
+ function () {
+ const { projects } = this.props;
+ if (projects && projects.first()) {
+ this.props.fetchMeta(projects.first().id);
+ }
+ }.bind(this)
+ );
}
- }
+ };
render() {
const {
- sessionId, isModalDisplayed, projectsLoading, metaLoading, fetchIssuesLoading, issuesIntegration
+ sessionId,
+ isModalDisplayed,
+ projectsLoading,
+ metaLoading,
+ fetchIssuesLoading,
+ issuesIntegration,
} = this.props;
- const provider = issuesIntegration.provider
+ const provider = issuesIntegration.provider;
return (
-
-
+
+
+
+
-
+
}
>
-
-
- Create Issue
-
+
+
+ Create Issue
+
-
+
+
);
}
-};
+}
export default Issues;
diff --git a/frontend/app/components/Session_/Subheader.js b/frontend/app/components/Session_/Subheader.js
index 9e10aaf45..446311a29 100644
--- a/frontend/app/components/Session_/Subheader.js
+++ b/frontend/app/components/Session_/Subheader.js
@@ -78,8 +78,6 @@ function SubHeader(props) {
);
}
-const SubH = connectPlayer(
- (state) => ({ currentLocation: state.location })
-)(SubHeader);
+const SubH = connectPlayer((state) => ({ currentLocation: state.location }))(SubHeader);
export default React.memo(SubH);
diff --git a/frontend/app/components/shared/SharePopup/SessionCopyLink/SessionCopyLink.tsx b/frontend/app/components/shared/SharePopup/SessionCopyLink/SessionCopyLink.tsx
index bc8c09830..fccbb6b81 100644
--- a/frontend/app/components/shared/SharePopup/SessionCopyLink/SessionCopyLink.tsx
+++ b/frontend/app/components/shared/SharePopup/SessionCopyLink/SessionCopyLink.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { IconButton } from 'UI';
+import { Button, Icon } from 'UI';
import copy from 'copy-to-clipboard';
import { connectPlayer } from 'Player';
@@ -7,12 +7,13 @@ interface Props {
content: string;
time: any;
}
+
function SessionCopyLink({ content = '', time }: Props) {
- const [copied, setCopied] = React.useState(false)
+ const [copied, setCopied] = React.useState(false);
const copyHandler = () => {
setCopied(true);
- copy(window.location.origin + window.location.pathname + '?jumpto=' + Math.round(time));
+ copy(window.location.origin + window.location.pathname + '?jumpto=' + Math.round(time));
setTimeout(() => {
setCopied(false);
}, 1000);
@@ -20,10 +21,25 @@ function SessionCopyLink({ content = '', time }: Props) {
return (
-
- { copied &&
Copied
}
+ {/*
*/}
+
+ <>
+
+ Copy URL at current time
+ >
+
+ {copied &&
Copied
}
- )
+ );
}
-export default SessionCopyLink
+const SessionCopyLinkCompo = connectPlayer((state: any) => ({
+ time: state.time,
+}))(SessionCopyLink);
+
+export default React.memo(SessionCopyLinkCompo);
diff --git a/frontend/app/components/shared/SharePopup/SharePopup.js b/frontend/app/components/shared/SharePopup/SharePopup.js
index 659deffb8..43e968479 100644
--- a/frontend/app/components/shared/SharePopup/SharePopup.js
+++ b/frontend/app/components/shared/SharePopup/SharePopup.js
@@ -13,9 +13,9 @@ import cn from 'classnames';
import { fetchList, init } from 'Duck/integrations/slack';
import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
-@connectPlayer((state) => ({
- time: state.time,
-}))
+// @connectPlayer((state) => ({
+// time: state.time,
+// }))
@connect(
(state) => ({
channels: state.getIn(['slack', 'list']),
@@ -58,7 +58,7 @@ export default class SharePopup extends React.PureComponent {
};
handleSuccess = () => {
- this.setState({ isOpen: false, comment: '' })
+ this.setState({ isOpen: false, comment: '' });
toast.success('Sent to Slack.');
};
@@ -69,31 +69,31 @@ export default class SharePopup extends React.PureComponent {
};
render() {
- const { trigger, loading, channels, showCopyLink = false, time } = this.props;
+ const { trigger, loading, channels, showCopyLink = false } = this.props;
const { comment, channelId, isOpen } = this.state;
const options = channels
.map(({ webhookId, name }) => ({ value: webhookId, label: name }))
.toJS();
return (
-
{
- this.setState({ isOpen: false })
- }}
- >
+ {
+ this.setState({ isOpen: false });
+ }}
+ >
+
Share this session link to Slack
@@ -105,7 +105,7 @@ export default class SharePopup extends React.PureComponent {
{showCopyLink && (
-
+
)}
>
@@ -132,7 +132,7 @@ export default class SharePopup extends React.PureComponent {
className="mr-4"
/>
-
+
{loading ? 'Sending...' : 'Send'}
@@ -142,16 +142,16 @@ export default class SharePopup extends React.PureComponent {
-
+
)}
-
- }
- >
- {trigger}
-
+ }
+ >
+ {trigger}
+
+
);
}
}
diff --git a/frontend/app/components/ui/AnimatedTooltip/AnimatedTooltip.tsx b/frontend/app/components/ui/AnimatedTooltip/AnimatedTooltip.tsx
new file mode 100644
index 000000000..784ee6464
--- /dev/null
+++ b/frontend/app/components/ui/AnimatedTooltip/AnimatedTooltip.tsx
@@ -0,0 +1,69 @@
+import React, { cloneElement, useMemo, useState } from 'react';
+import {
+ Placement,
+ offset,
+ flip,
+ shift,
+ autoUpdate,
+ useFloating,
+ useInteractions,
+ useHover,
+ useFocus,
+ useRole,
+ useDismiss,
+ useClick,
+} from '@floating-ui/react-dom-interactions';
+import { mergeRefs } from 'react-merge-refs';
+
+interface Props {
+ label: string;
+ placement?: Placement;
+ children: JSX.Element;
+}
+
+function AnimatedTooltip({ children, label, placement = 'top' }: Props) {
+ const [open, setOpen] = useState(false);
+
+ const { x, y, reference, floating, strategy, context } = useFloating({
+ placement,
+ open,
+ onOpenChange: setOpen,
+ middleware: [offset(5), flip(), shift({ padding: 8 })],
+ whileElementsMounted: autoUpdate,
+ });
+
+ const { getReferenceProps, getFloatingProps } = useInteractions([
+ // useHover(context),
+ useFocus(context),
+ useRole(context, { role: 'tooltip' }),
+ useDismiss(context),
+ useClick(context),
+ ]);
+
+ // Preserve the consumer's ref
+ const ref = useMemo(() => mergeRefs([reference, (children as any).ref]), [reference, children]);
+ const ppp = getReferenceProps({ ref, ...children.props });
+ // console.log('ppp', ppp);
+ return (
+
+ {/* {cloneElement(children, getReferenceProps({ ref, ...children.props }))} */}
+
Button
+ {open && (
+
+ {label}
+
+ )}
+
+ );
+}
+
+export default AnimatedTooltip;
diff --git a/frontend/app/components/ui/AnimatedTooltip/index.ts b/frontend/app/components/ui/AnimatedTooltip/index.ts
new file mode 100644
index 000000000..5ed196fd5
--- /dev/null
+++ b/frontend/app/components/ui/AnimatedTooltip/index.ts
@@ -0,0 +1 @@
+export { default } from './AnimatedTooltip';
diff --git a/frontend/app/components/ui/index.js b/frontend/app/components/ui/index.js
index db7ea7d37..11cd00f1d 100644
--- a/frontend/app/components/ui/index.js
+++ b/frontend/app/components/ui/index.js
@@ -57,3 +57,4 @@ export { default as Input } from './Input';
export { default as Form } from './Form';
export { default as Modal } from './Modal';
export { default as Message } from './Message';
+export { default as AnimatedTooltip } from './AnimatedTooltip';
\ No newline at end of file