Skip to content

Commit c536e51

Browse files
Tim Roesedmundito
andauthored
Fix copy link to logs functionality (#15368)
* Fix copy link to logs functionality * Update airbyte-webapp/src/components/JobItem/JobItem.tsx Co-authored-by: Edmundo Ruiz Ghanem <168664+edmundito@users.noreply.github.com> * Fix scrolling * Remove smooth scrolling * Improve effect for better return statements * Better scroll Co-authored-by: Edmundo Ruiz Ghanem <168664+edmundito@users.noreply.github.com>
1 parent 62303a8 commit c536e51

File tree

5 files changed

+42
-45
lines changed

5 files changed

+42
-45
lines changed

airbyte-webapp/src/components/JobItem/JobItem.tsx

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import React, { Suspense, useRef, useState } from "react";
2-
import { useEffectOnce } from "react-use";
1+
import React, { Suspense, useCallback, useRef, useState } from "react";
32
import styled from "styled-components";
43

54
import { Spinner } from "components";
65

76
import { SynchronousJobReadWithStatus } from "core/request/LogsRequestError";
87
import { JobsWithJobs } from "pages/ConnectionPage/pages/ConnectionItemPage/components/JobsList";
98

10-
import { AttemptRead, JobStatus } from "../../core/request/AirbyteClient";
9+
import { AttemptRead, CheckConnectionReadStatus, JobStatus } from "../../core/request/AirbyteClient";
1110
import { useAttemptLink } from "./attemptLinkUtils";
1211
import ContentWrapper from "./components/ContentWrapper";
1312
import ErrorDetails from "./components/ErrorDetails";
@@ -32,28 +31,29 @@ const LoadLogs = styled.div`
3231
`;
3332

3433
interface JobItemProps {
35-
shortInfo?: boolean;
3634
job: SynchronousJobReadWithStatus | JobsWithJobs;
3735
}
3836

3937
const didJobSucceed = (job: SynchronousJobReadWithStatus | JobsWithJobs) => {
4038
return getJobStatus(job) !== "failed";
4139
};
4240

43-
export const getJobStatus: (job: SynchronousJobReadWithStatus | JobsWithJobs) => JobStatus = (job) => {
44-
return (job as JobsWithJobs).job?.status ?? (job as SynchronousJobReadWithStatus).status;
41+
export const getJobStatus: (
42+
job: SynchronousJobReadWithStatus | JobsWithJobs
43+
) => JobStatus | CheckConnectionReadStatus = (job) => {
44+
return "status" in job ? job.status : job.job.status;
4545
};
4646

4747
export const getJobAttemps: (job: SynchronousJobReadWithStatus | JobsWithJobs) => AttemptRead[] | undefined = (job) => {
4848
return "attempts" in job ? job.attempts : undefined;
4949
};
5050

51-
export const getJobId = (job: SynchronousJobReadWithStatus | JobsWithJobs) =>
52-
(job as SynchronousJobReadWithStatus).id ?? (job as JobsWithJobs).job.id;
51+
export const getJobId = (job: SynchronousJobReadWithStatus | JobsWithJobs) => ("id" in job ? job.id : job.job.id);
5352

54-
export const JobItem: React.FC<JobItemProps> = ({ shortInfo, job }) => {
53+
export const JobItem: React.FC<JobItemProps> = ({ job }) => {
5554
const { jobId: linkedJobId } = useAttemptLink();
56-
const [isOpen, setIsOpen] = useState(linkedJobId === getJobId(job));
55+
const alreadyScrolled = useRef(false);
56+
const [isOpen, setIsOpen] = useState(() => linkedJobId === String(getJobId(job)));
5757
const scrollAnchor = useRef<HTMLDivElement>(null);
5858

5959
const didSucceed = didJobSucceed(job);
@@ -62,26 +62,20 @@ export const JobItem: React.FC<JobItemProps> = ({ shortInfo, job }) => {
6262
setIsOpen(!isOpen);
6363
};
6464

65-
useEffectOnce(() => {
66-
if (linkedJobId) {
67-
scrollAnchor.current?.scrollIntoView({
68-
behavior: "smooth",
69-
block: "start",
70-
});
65+
const onDetailsToggled = useCallback(() => {
66+
if (alreadyScrolled.current || linkedJobId !== String(getJobId(job))) {
67+
return;
7168
}
72-
});
69+
scrollAnchor.current?.scrollIntoView({
70+
block: "start",
71+
});
72+
alreadyScrolled.current = true;
73+
}, [job, linkedJobId]);
7374

7475
return (
7576
<Item isFailed={!didSucceed} ref={scrollAnchor}>
76-
<MainInfo
77-
shortInfo={shortInfo}
78-
isOpen={isOpen}
79-
isFailed={!didSucceed}
80-
onExpand={onExpand}
81-
job={job}
82-
attempts={getJobAttemps(job)}
83-
/>
84-
<ContentWrapper isOpen={isOpen}>
77+
<MainInfo isOpen={isOpen} isFailed={!didSucceed} onExpand={onExpand} job={job} attempts={getJobAttemps(job)} />
78+
<ContentWrapper isOpen={isOpen} onToggled={onDetailsToggled}>
8579
<div>
8680
<Suspense
8781
fallback={

airbyte-webapp/src/components/JobItem/components/ContentWrapper.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import React from "react";
44
interface IProps {
55
children?: React.ReactNode;
66
isOpen?: boolean;
7+
onToggled?: () => void;
78
}
89

9-
const ContentWrapper: React.FC<IProps> = ({ children, isOpen }) => {
10+
const ContentWrapper: React.FC<IProps> = ({ children, isOpen, onToggled }) => {
1011
return (
1112
<motion.div
1213
animate={!isOpen ? "closed" : "open"}
14+
onAnimationComplete={onToggled}
1315
variants={{
1416
open: {
1517
height: "auto",

airbyte-webapp/src/components/JobItem/components/MainInfo.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,24 @@ interface MainInfoProps {
3434
isOpen?: boolean;
3535
onExpand: () => void;
3636
isFailed?: boolean;
37-
shortInfo?: boolean;
3837
}
3938

40-
const MainInfo: React.FC<MainInfoProps> = ({ job, attempts = [], isOpen, onExpand, isFailed, shortInfo }) => {
39+
const MainInfo: React.FC<MainInfoProps> = ({ job, attempts = [], isOpen, onExpand, isFailed }) => {
4140
const jobStatus = getJobStatus(job);
4241
const isPartialSuccess = partialSuccessCheck(attempts);
4342

4443
const statusIcon = () => {
4544
switch (true) {
4645
case jobStatus === JobStatus.cancelled:
47-
return <StatusIcon />;
46+
return <StatusIcon status="error" />;
4847
case jobStatus === JobStatus.running:
4948
return <StatusIcon status="loading" />;
5049
case jobStatus === JobStatus.succeeded:
5150
return <StatusIcon status="success" />;
5251
case isPartialSuccess:
5352
return <StatusIcon status="warning" />;
54-
case !isPartialSuccess && isFailed && !shortInfo:
55-
return <StatusIcon />;
53+
case !isPartialSuccess && isFailed:
54+
return <StatusIcon status="error" />;
5655
default:
5756
return null;
5857
}
@@ -71,8 +70,7 @@ const MainInfo: React.FC<MainInfoProps> = ({ job, attempts = [], isOpen, onExpan
7170
) : (
7271
<FormattedMessage id={`sources.${getJobStatus(job)}`} />
7372
)}
74-
{shortInfo && <FormattedMessage id="sources.additionLogs" />}
75-
{attempts.length && !shortInfo && (
73+
{attempts.length && (
7674
<>
7775
{attempts.length > 1 && (
7876
<div className={styles.lastAttempt}>

airbyte-webapp/src/components/StatusIcon/StatusIcon.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ describe("<StatusIcon />", () => {
2020
{ status: "sleep", icon: "moon" },
2121
{ status: "warning", icon: "triangle-exclamation" },
2222
{ status: "loading", icon: "circle-loader" },
23+
{ status: "error", icon: "xmark" },
2324
];
2425

2526
test.each(statusCases)("renders $status status", ({ status, icon }) => {

airbyte-webapp/src/components/StatusIcon/StatusIcon.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { faBan, faCheck, faExclamationTriangle, faTimes, IconDefinition } from "@fortawesome/free-solid-svg-icons";
1+
import { faBan, faCheck, faExclamationTriangle, faTimes } from "@fortawesome/free-solid-svg-icons";
22
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
33
import React from "react";
44
import styled from "styled-components";
@@ -8,7 +8,7 @@ import { MoonIcon } from "components/icons/MoonIcon";
88
import PauseIcon from "../icons/PauseIcon";
99
import CircleLoader from "./CircleLoader";
1010

11-
export type StatusIconStatus = "sleep" | "inactive" | "success" | "warning" | "loading";
11+
export type StatusIconStatus = "sleep" | "inactive" | "success" | "warning" | "loading" | "error";
1212

1313
interface StatusIconProps {
1414
className?: string;
@@ -20,20 +20,22 @@ interface StatusIconProps {
2020

2121
const getBadgeWidth = (props: StatusIconProps) => (props.big ? (props.value ? 57 : 40) : props.value ? 37 : 20);
2222

23-
const _iconByStatus: Partial<Record<StatusIconStatus, IconDefinition | undefined>> = {
23+
const _iconByStatus = {
2424
sleep: faBan,
2525
success: faCheck,
2626
warning: faExclamationTriangle,
27-
};
27+
error: faTimes,
28+
} as const;
2829

29-
const _themeByStatus: Partial<Record<StatusIconStatus, string>> = {
30+
const _themeByStatus = {
3031
sleep: "lightTextColor",
3132
inactive: "lightTextColor",
3233
success: "successColor",
3334
warning: "warningColor",
34-
};
35+
error: "dangerColor",
36+
} as const;
3537

36-
const Container = styled.div<StatusIconProps>`
38+
const Container = styled.div<Pick<StatusIconProps, "big" | "value">>`
3739
width: ${(props) => getBadgeWidth(props)}px;
3840
height: ${({ big }) => (big ? 40 : 20)}px;
3941
margin-right: 10px;
@@ -44,8 +46,8 @@ const Container = styled.div<StatusIconProps>`
4446
vertical-align: middle;
4547
`;
4648

47-
const Badge = styled(Container)<StatusIconProps>`
48-
background: ${(props) => props.theme[(props.status && _themeByStatus[props.status]) || "dangerColor"]};
49+
const Badge = styled(Container)<{ status: Exclude<StatusIconStatus, "loading"> }>`
50+
background: ${({ theme, status }) => theme[_themeByStatus[status]]};
4951
border-radius: ${({ value }) => (value ? "15px" : "50%")};
5052
color: ${({ theme }) => theme.whiteColor};
5153
padding-top: ${({ status }) => (status === "warning" || status === "inactive" ? 3 : 4)}px;
@@ -66,7 +68,7 @@ const Value = styled.span`
6668
padding-left: 3px;
6769
`;
6870

69-
const StatusIcon: React.FC<StatusIconProps> = ({ title, status, ...props }) => {
71+
const StatusIcon: React.FC<StatusIconProps> = ({ title, status = "error", ...props }) => {
7072
const valueElement = props.value ? <Value>{props.value}</Value> : null;
7173

7274
if (status === "loading") {
@@ -85,7 +87,7 @@ const StatusIcon: React.FC<StatusIconProps> = ({ title, status, ...props }) => {
8587
) : status === "sleep" ? (
8688
<MoonIcon title={title} />
8789
) : (
88-
<FontAwesomeIcon icon={(status && _iconByStatus[status]) || faTimes} title={title} />
90+
<FontAwesomeIcon icon={_iconByStatus[status]} title={title} />
8991
)}
9092
{valueElement}
9193
</Badge>

0 commit comments

Comments
 (0)