Skip to content

Fixes #33112: restore delete, hover affordance and expand in task com… - #33178

Open
Vansh0310 wants to merge 9 commits into
open-metadata:mainfrom
Vansh0310:fix/incident-comment-feed-33112
Open

Fixes #33112: restore delete, hover affordance and expand in task com…#33178
Vansh0310 wants to merge 9 commits into
open-metadata:mainfrom
Vansh0310:fix/incident-comment-feed-33112

Conversation

@Vansh0310

@Vansh0310 Vansh0310 commented Sep 11, 2026

Copy link
Copy Markdown
Contributor
Screenshot 2026-09-11 at 11 18 53 AM

Describe your changes:

Fixes #33112

Adds the ability to delete an individual task comment. The comment's author or an
admin can now delete a comment via a trash icon that appears on hover next to the
comment, with a confirmation dialog before the delete goes through.

Also fixes two pre-existing bugs in the comment thread surfaced while building this:

  1. The delete icon was originally laid out as a normal flex sibling next to a
    full-width Space, which had no room for it — hovering over any comment caused
    the comment text to visibly reflow/shrink and then snap back. The icon is now
    absolutely positioned over the content (matching the existing pattern in
    CommentCard.component.tsx / ActivityFeedActions.tsx), so hovering no longer
    moves anything.
  2. Long comments were being cut off mid-sentence with no way to read the rest.
    RichTextEditorPreviewNew clamps text to ~2 lines regardless of the
    enableSeeMoreVariant prop — that prop only controls whether the "View
    More"/"View Less" toggle renders, not whether the clamp applies. An incorrect
    enableSeeMoreVariant={false} on this component suppressed the toggle while the
    clamp stayed active. Removed it so the toggle now appears whenever a comment
    overflows.

Type of change:

  • New feature (non-breaking change which adds functionality)
  • Bug fix

High-level design:

TaskCommentCard now accepts currentUser and an onCommentDeleted callback from
TaskTabNew. A canDelete check (author match OR currentUser.isAdmin) gates a
hover-only delete icon; clicking it opens the shared DeleteModal, which on confirm
calls deleteTaskComment(taskId, commentId) and, on success, calls
onCommentDeleted (wired to fetchUpdatedThread(task.id, true) in TaskTabNew to
refresh the thread) and closes the modal. Failures show an error toast and leave the
modal open so the user can retry.

Tests:

Use cases covered

  • Comment author can delete their own comment; other non-admin users cannot.
  • Admins can delete any comment regardless of authorship.
  • Hovering a comment no longer shifts/reflows its text.
  • A long comment now shows a working "View More"/"View Less" toggle instead of
    being silently truncated.

Unit tests

  • I added unit tests for the new/changed logic.
  • Files added/updated: TaskCommentCard.component.test.tsx (new),
    TaskTabNew.component.test.tsx (extended to cover the currentUser /
    onCommentDeleted wiring).

Backend integration tests

  • Not applicable (no backend API changes — reuses the existing
    DELETE .../tasks/{taskId}/comments/{commentId} endpoint and deleteTaskComment
    client function, both already in place).

Ingestion integration tests

  • Not applicable (no ingestion changes).

Playwright (UI) tests

  • Not yet added — consider a Playwright case covering hover → delete → confirm →
    comment removed from the thread.

Manual testing performed

  1. Opened a task with multiple comments as the comment author — confirmed the
    delete icon appears on hover only for my own comments, and deleting removes the
    comment and refreshes the thread.
  2. Logged in as an admin — confirmed the delete icon appears on every comment
    regardless of author.
  3. Logged in as a different, non-admin user — confirmed no delete icon appears on
    comments authored by others.
  4. Hovered over comments repeatedly — confirmed no text reflow/jump.
  5. Posted a long comment — confirmed "View More"/"View Less" now works instead of
    the text cutting off.

UI screen recording / screenshots:

[attach before/after — the reflow-on-hover and the cut-off-text cases are worth
showing explicitly since they're easy to miss in a still screenshot]

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • My PR is linked to a GitHub issue via Fixes #<issue-number> above.
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: not applicable, no schema changes.
  • For UI changes: screenshots/recording attached above.
  • I have added tests and listed them above.

…d in task comments

Incident task comments had no way to delete a comment, no visible affordance
for the action, and long comments were clamped with no way to expand them.

- Drop enableSeeMoreVariant={false} on the comment previewer so it falls back
  to its default of true and long comments get a working view-more toggle.
- Reveal a delete action on hover for the comment author or an admin, wired to
  deleteTaskComment through the shared DeleteModal. It is positioned out of
  flow so showing it cannot reflow the comment body.
- Pass currentUser down from TaskTabNew for the permission check, and refetch
  the thread via onCommentDeleted so the list reflects the delete.

Adds TaskCommentCard.test.tsx covering rendering, both regression guards, the
four permission combinations, hover behaviour and the delete flow including the
failure path. Extends the TaskTabNew suite to confirm the two new props are
wired through.
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

The delete affordance was an Ant Design Icon with an onClick, mounted only
while the mouse was over the card. It had no button role, no tab stop and no
accessible name, so keyboard and screen-reader users could never reach it.

- Replace it with ButtonUtility from ui-core-components, which renders a real
  button via react-aria and takes an accessible name from its tooltip.
- Keep it mounted whenever the user may delete, and reveal it with CSS on card
  hover or on its own focus instead of a mouse-only hover state. This drops the
  isHovered state and the wrapper's role="presentation".
- Move the inline positioning styles onto Tailwind utilities.

Covered by tests asserting the button role and accessible name, that it is
focusable without a hover, and that Tab followed by Enter opens the modal.
Main added a rule that icons come from @openmetadata/ui-core-components/icons
and must not be imported directly from assets/ paths, since bypassing the
re-export can diverge on version upgrades. Swap the raw ic-delete.svg import
for the Delete icon the package exports.
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@anuj-kumary anuj-kumary added the safe to test Add this label to run secure Github workflows on PRs label Sep 11, 2026
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 70%
70.94% (96392/135865) 55.64% (57287/102943) 56.95% (19074/33487)

@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@gitar-bot

gitar-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 5 resolved / 5 findings

Restores delete, hover affordance, and expand functionality in task comments. Delete icon now appears on hover for comment authors and admins, with a confirmation dialog. Also fixes two pre-existing bugs: hover no longer causes text reflow, and long comments now show a working "View More"/"View Less" toggle instead of being silently truncated. All findings addressed: delete action is now keyboard and screen-reader accessible, the delete icon uses core-components, DeleteModal z-index is scoped to avoid affecting other components, modal mounting is conditional, and container mutations are avoided.

✅ 5 resolved
Quality: Delete affordance is not keyboard/screen-reader accessible

📄 openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/TaskCommentCard.component.tsx:121-134
The delete control is gated on isHovered (mouse-only) and rendered as an Ant Design <Icon onClick> with no button role, no focusability (tabindex), and no accessible name (only a data-testid). Keyboard and screen-reader users can never reach or trigger the delete action, violating the a11y requirement to use semantic elements with keyboard access. Render it as a real <button> (or the library's IconButton) that is also revealed on focus, and give it an aria-label/Tooltip title (e.g. t('label.delete')).

Quality: New delete widget uses Ant Design Icon instead of core-components

📄 openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/TaskCommentCard.component.tsx:14 📄 openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/TaskCommentCard.component.tsx:121-134
New UI work should use @openmetadata/ui-core-components and avoid hand-rolling clickable widgets with Ant Design. The delete affordance is built from @ant-design/icons Icon with an inline onClick and inline styles rather than a library button component. Consider using the core-components button/icon-button so behavior, focus, and styling stay consistent (this also resolves the accessibility gap above).

Quality: Global DeleteModal z-index bump affects all 75 consumers

📄 openmetadata-ui/src/main/resources/ui/src/components/common/DeleteModal/DeleteModal.tsx:42-45
The z-index of the shared DeleteModal was raised from 999 to 1001 to sit above an antd Drawer mask in the task-comment case, but DeleteModal is used in ~75 files across the app, so this stacking change now applies everywhere, not just inside the drawer. Consider making the elevated z-index opt-in (e.g. a prop passed by TaskCommentCard) so the fix is scoped to the drawer context and cannot regress stacking in the many other consumers.

Quality: DeleteModal mounted for every comment even when canDelete is false

📄 openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/TaskCommentCard.component.tsx:149-163
<DeleteModal> is rendered unconditionally for each TaskCommentCard, outside the canDelete guard, so a thread with many comments mounts one (closed) modal overlay per comment even for users who can never open it. Since showDeleteDialog can only be set true by the delete button (which only renders when canDelete), move the modal inside the canDelete && (...) block to avoid the redundant overlays.

Quality: Parent tabindex fallback permanently mutates shared container

📄 openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/TaskCommentCard.component.tsx:97-103
When a deleted comment is the only/last one, the unmount cleanup sets card.parentElement.setAttribute('tabindex', '-1') on the shared replies container and never removes it, leaving the container permanently in the tab/focus order for the lifetime of the thread. Because the cleanup runs on any unmount (route change, drawer close, list re-render), this can also fire during teardown. Consider using a ref to the replies container that already carries a stable tabIndex={-1}, or restoring/removing the attribute, rather than mutating a foreign parent node on unmount.

Options

Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@anuj-kumary anuj-kumary added the safe to test Add this label to run secure Github workflows on PRs label Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

❌ UI Checkstyle Failed

❌ Playwright - Guardrails + ESLint + Prettier + Organise Imports

Either a Playwright test file has linting/formatting issues, or a guardrail check failed: ESLint rule unit tests, a new guardrail violation, a stale suppression entry (its violation was fixed but the baseline was not pruned), a blanket eslint-disable, or a stale generated rule table. For the guardrail cases run yarn lint:playwright:suppressions in openmetadata-ui/src/main/resources/ui and commit the pruned eslint-suppressions.json, then yarn test:eslint-rules && node scripts/generate-playwright-rule-table.mjs --check.

Affected files

Subtest: const a = 'server.entity-fetch-error';\n const b = 'server.entity-fetch-error';\n const c = 'server.entity-fetch-error';

    ok 2 - const a = 'server.entity-fetch-error';\\n             const b = 'server.entity-fetch-error';\\n             const c = 'server.entity-fetch-error';

not ok 27 - the suppressions baseline matches its recorded state exactly
failureType: 'testCodeFailure'
error: |-
name: 'AssertionError'

fail 1

error Command failed with exit code 1.

🔍 ESLint findings in this PR's files — 0 error(s), 27 warning(s)

Errors block the build. Warnings do not yet — they are rules whose backlog is still
being worked down, listed so this PR does not add to it. See docs/ui-code-quality-gate.md.

0 error(s), 27 warning(s) across 2 changed file(s).

Count Rule
12 no-restricted-imports
10 react-hooks/exhaustive-deps
3 openmetadata-imports/review-sequential-api-calls
2 openmetadata-imports/no-lower-layer-page-imports
All findings
Location Rule Message
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:51:1 no-restricted-imports '../../../../assets/svg/ic-assignees.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the design
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:54:1 no-restricted-imports '../../../../assets/svg/ic-close-task.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the desig
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:55:1 no-restricted-imports '../../../../assets/svg/ic-open-task.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the design
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:56:1 no-restricted-imports '../../../../assets/svg/ic-user-profile.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the des
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:59:1 no-restricted-imports '../../../../assets/svg/ic_access-level.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the des
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:60:1 no-restricted-imports '../../../../assets/svg/ic_access-type.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the desi
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:61:1 no-restricted-imports '../../../../assets/svg/ic_column-requested.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:62:1 no-restricted-imports '../../../../assets/svg/ic_duration.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the designa
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:63:1 no-restricted-imports '../../../../assets/svg/ic_reason-access.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the de
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:64:1 no-restricted-imports '../../../../assets/svg/ic_ticket.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the designate
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:65:1 no-restricted-imports '../../../../assets/svg/plus-colored.svg' import is restricted from being used by a pattern. Do not import SVG icons directly from assets/ paths; use the design
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:82:1 openmetadata-imports/no-lower-layer-page-imports Pages are route-level composition modules. Move the shared implementation/type to a lower layer instead of importing a page from here.
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:83:1 openmetadata-imports/no-lower-layer-page-imports Pages are route-level composition modules. Move the shared implementation/type to a lower layer instead of importing a page from here.
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:603:6 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array.
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:770:9 react-hooks/exhaustive-deps The 'handleTaskLinkClick' function makes the dependencies of useMemo Hook (at line 803) change on every render. Move it inside the useMemo callback. Alternative
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:841:25 openmetadata-imports/review-sequential-api-calls Review these sequential API requests. If they are independent, start them together with Promise.all/Promise.allSettled; keep sequencing only when data-dependent
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:924:5 react-hooks/exhaustive-deps React Hook useCallback has a missing dependency: 'updateTaskData'. Either include it or remove the dependency array.
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:1054:9 react-hooks/exhaustive-deps The 'onTaskReject' function makes the dependencies of useMemo Hook (at line 1818) change on every render. To fix this, wrap the definition of 'onTaskReject' in
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:1144:25 openmetadata-imports/review-sequential-api-calls Review these sequential API requests. If they are independent, start them together with Promise.all/Promise.allSettled; keep sequencing only when data-dependent
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:1178:25 openmetadata-imports/review-sequential-api-calls Review these sequential API requests. If they are independent, start them together with Promise.all/Promise.allSettled; keep sequencing only when data-dependent
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:1275:6 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array.
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:1419:6 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array.
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:1463:6 react-hooks/exhaustive-deps React Hook useMemo has missing dependencies: 'isActionLoading', 'isCreator', 'onTestCaseTaskDropdownClick', and 'permissions'. Either include them or remove the
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:1539:6 react-hooks/exhaustive-deps React Hook useMemo has missing dependencies: 'hasEditAccess', 'noSuggestionTaskMenuOptions', and 't'. Either include them or remove the dependency array.
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:1590:6 react-hooks/exhaustive-deps React Hook useEffect has a missing dependency: 'assigneesForm'. Either include it or remove the dependency array.
🟡 src/components/Entity/Task/TaskTab/TaskTabNew.component.tsx:1790:9 react-hooks/exhaustive-deps The 'closeFeedEditor' function makes the dependencies of useMemo Hook (at line 1858) change on every render. To fix this, wrap the definition of 'closeFeedEdito
🟡 src/components/common/DeleteModal/DeleteModal.tsx:23:1 no-restricted-imports '@untitledui/icons' import is restricted from being used by a pattern. Import icons from @openmetadata/ui-core-components/icons, not directly from @untitledui/i

Fix locally (fast - only checks files changed in this branch):

make ui-checkstyle-changed

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incidents window: comment delete button missing, comments not clickable, long comments truncated with no expand option

3 participants