This is an automated email from the ASF dual-hosted git repository.
voidmatcha pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git
The following commit(s) were added to refs/heads/master by this push:
new 52397c8cd5 [ZEPPELIN-6443] Avoid misleading elapsed-time footer when a
running paragraph has no dateStarted
52397c8cd5 is described below
commit 52397c8cd585fd821837432ff94c2b49f4ba2fc5
Author: 김예나 <[email protected]>
AuthorDate: Wed Jul 22 00:02:43 2026 +0900
[ZEPPELIN-6443] Avoid misleading elapsed-time footer when a running
paragraph has no dateStarted
### What is this PR for?
The Angular paragraph footer fell back to `new Date()` when `dateStarted`
was undefined for a running paragraph, which rendered a misleading `Started
less than a minute ago.` message — implying the run had just started when the
real start time was simply unknown (a race where the paragraph is `RUNNING`
before `dateStarted` is populated).
This PR shows a neutral `Running…` label instead when the start time is
unknown, and keeps the existing `Started X ago.` output when `dateStarted` is
present. The same bug/fix applies to the React `ParagraphFooter` twin, and a
unit test is added there for the missing-`dateStarted` case (the Angular
project has no unit-test setup).
The pre-existing `TODO(hsuanxyz) dateStarted undefined after start` is
resolved.
### What type of PR is it?
Bug Fix
### What is the Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-6443
### How should this be tested?
- `cd zeppelin-web-angular && npm run lint`
- React unit tests: `cd zeppelin-web-angular/projects/zeppelin-react && npm
test` (covers the `Running…` / missing-`dateStarted` case)
- Manually: run a paragraph and confirm the running (elapsed-time) and
finished (execution-time) footers still render correctly.
### Screenshots (if appropriate)
N/A
### Questions:
- Does the licenses files need to update? No
- Is there breaking changes for older versions? No
- Does this needs documentation? No
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Closes #5330 from kimyenac/ZEPPELIN-6443.
Signed-off-by: YONGJAE LEE <[email protected]>
---
.../components/paragraph/ParagraphFooter.spec.tsx | 14 ++++++++++++
.../src/components/paragraph/ParagraphFooter.tsx | 25 +++++++++-------------
.../notebook/paragraph/footer/footer.component.ts | 9 ++++++--
3 files changed, 31 insertions(+), 17 deletions(-)
diff --git
a/zeppelin-web-angular/projects/zeppelin-react/src/components/paragraph/ParagraphFooter.spec.tsx
b/zeppelin-web-angular/projects/zeppelin-react/src/components/paragraph/ParagraphFooter.spec.tsx
index 69d1476b4f..43e5bec30a 100644
---
a/zeppelin-web-angular/projects/zeppelin-react/src/components/paragraph/ParagraphFooter.spec.tsx
+++
b/zeppelin-web-angular/projects/zeppelin-react/src/components/paragraph/ParagraphFooter.spec.tsx
@@ -96,6 +96,20 @@ describe('ParagraphFooter mount contract', () => {
}
});
+ it('renders a neutral "Running…" label while running without a dateStarted',
() => {
+ mountFooter({
+ ...baseProps,
+ dateStarted: undefined,
+ showExecutionTime: false,
+ showElapsedTime: true
+ });
+
+ // A missing dateStarted must not produce a misleading "Started 0 seconds
ago" message.
+ const elapsedTime = host!.querySelector('.elapsed-time')!;
+ expect(elapsedTime.textContent).toBe('Running…');
+ expect(elapsedTime.textContent).not.toMatch(/Started/);
+ });
+
it('update() re-renders in place with new props', () => {
mountFooter(baseProps);
expect(host!.querySelector('.execution-time')).not.toBeNull();
diff --git
a/zeppelin-web-angular/projects/zeppelin-react/src/components/paragraph/ParagraphFooter.tsx
b/zeppelin-web-angular/projects/zeppelin-react/src/components/paragraph/ParagraphFooter.tsx
index d73f881653..1adec800f5 100644
---
a/zeppelin-web-angular/projects/zeppelin-react/src/components/paragraph/ParagraphFooter.tsx
+++
b/zeppelin-web-angular/projects/zeppelin-react/src/components/paragraph/ParagraphFooter.tsx
@@ -26,11 +26,7 @@ export interface ParagraphFooterProps {
}
const isOutdated = (dateUpdated?: string, dateStarted?: string): boolean => {
- return (
- dateUpdated !== undefined &&
- dateStarted !== undefined &&
- Date.parse(dateUpdated) > Date.parse(dateStarted)
- );
+ return dateUpdated !== undefined && dateStarted !== undefined &&
Date.parse(dateUpdated) > Date.parse(dateStarted);
};
const computeExecutionTime = (props: ParagraphFooterProps): string => {
@@ -43,10 +39,7 @@ const computeExecutionTime = (props: ParagraphFooterProps):
string => {
return isOutdated(dateUpdated, dateStarted) ? 'outdated' : '';
}
- const durationFormat = formatDistanceStrict(
- new Date(dateStarted),
- new Date(dateFinished)
- );
+ const durationFormat = formatDistanceStrict(new Date(dateStarted), new
Date(dateFinished));
const endFormat = format(new Date(dateFinished), 'MMMM dd yyyy, h:mm:ss a');
const userLabel = user === undefined || user === null ? 'anonymous' : user;
let desc = `Took ${durationFormat}. Last updated by ${userLabel} at
${endFormat}.`;
@@ -57,8 +50,13 @@ const computeExecutionTime = (props: ParagraphFooterProps):
string => {
};
const computeElapsedTime = (dateStarted?: string): string => {
- const base = dateStarted ? new Date(dateStarted) : new Date();
- return `Started ${formatDistanceToNow(base)} ago.`;
+ // A running paragraph may not have a dateStarted yet (e.g. queued/pending
on the
+ // interpreter). Fall back to a neutral label instead of measuring from
"now", which
+ // would render a misleading "Started less than a minute ago." message.
+ if (!dateStarted) {
+ return 'Running…';
+ }
+ return `Started ${formatDistanceToNow(new Date(dateStarted))} ago.`;
};
export const ParagraphFooter = (props: ParagraphFooterProps) => {
@@ -79,10 +77,7 @@ export interface ParagraphFooterMountHandle {
unmount: () => void;
}
-export const mount = (
- element: HTMLElement,
- initialProps: ParagraphFooterProps
-): ParagraphFooterMountHandle => {
+export const mount = (element: HTMLElement, initialProps:
ParagraphFooterProps): ParagraphFooterMountHandle => {
if (!element) {
throw new Error('Mount element is required');
}
diff --git
a/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/footer/footer.component.ts
b/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/footer/footer.component.ts
index 6495742e26..7441edd2d5 100644
---
a/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/footer/footer.component.ts
+++
b/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/footer/footer.component.ts
@@ -67,8 +67,13 @@ export class NotebookParagraphFooterComponent implements
OnChanges {
}
getElapsedTime() {
- // TODO(hsuanxyz) dateStarted undefined after start
- return `Started ${formatDistanceToNow(this.dateStarted ? new
Date(this.dateStarted) : new Date())} ago.`;
+ // A running paragraph may not have a dateStarted yet (e.g. queued/pending
on the
+ // interpreter). Fall back to a neutral label instead of measuring from
"now", which
+ // would render a misleading "Started less than a minute ago." message.
+ if (!this.dateStarted) {
+ return 'Running…';
+ }
+ return `Started ${formatDistanceToNow(new Date(this.dateStarted))} ago.`;
}
constructor() {}