Skip to content

Commit b05550a

Browse files
authored
Merge pull request #3923 from SalmanNajah/add-tooltip
Add Tooltip for Remove from Collection List Button
2 parents e0deabe + 99a8fa6 commit b05550a

File tree

3 files changed

+134
-7
lines changed

3 files changed

+134
-7
lines changed

client/modules/User/components/CollectionItemRow.jsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useDispatch } from 'react-redux';
66
import { removeFromCollection } from '../../IDE/actions/collections';
77
import { formatDateToString } from '../../../utils/formatDate';
88
import RemoveIcon from '../../../images/close.svg';
9+
import { Tooltip } from '../../../common/Tooltip';
910

1011
const CollectionItemRow = ({ collection, item, isOwner }) => {
1112
const { t } = useTranslation();
@@ -48,13 +49,15 @@ const CollectionItemRow = ({ collection, item, isOwner }) => {
4849
<td>{sketchOwnerUsername}</td>
4950
<td className="collection-row__action-column">
5051
{isOwner && (
51-
<button
52-
className="collection-row__remove-button"
53-
onClick={handleSketchRemove}
54-
aria-label={t('Collection.SketchRemoveARIA')}
55-
>
56-
<RemoveIcon focusable="false" aria-hidden="true" />
57-
</button>
52+
<Tooltip content={t('Collection.SketchRemoveARIA')}>
53+
<button
54+
className="collection-row__remove-button"
55+
onClick={handleSketchRemove}
56+
aria-label={t('Collection.SketchRemoveARIA')}
57+
>
58+
<RemoveIcon focusable="false" aria-hidden="true" />
59+
</button>
60+
</Tooltip>
5861
)}
5962
</td>
6063
</tr>
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import React from 'react';
2+
import thunk from 'redux-thunk';
3+
import configureStore from 'redux-mock-store';
4+
import { reduxRender, screen, fireEvent, act } from '../../../test-utils';
5+
import { initialTestState } from '../../../testData/testReduxStore';
6+
import CollectionItemRow from './CollectionItemRow';
7+
8+
jest.mock('../../../i18n');
9+
10+
const mockStore = configureStore([thunk]);
11+
const store = mockStore(initialTestState);
12+
13+
let subjectProps = {
14+
collection: {
15+
id: 'collection-123',
16+
name: 'Test Collection'
17+
},
18+
item: {
19+
createdAt: '2026-01-15T10:30:00.000Z',
20+
isDeleted: false,
21+
project: {
22+
id: 'project-456',
23+
name: 'My Sketch',
24+
user: { username: 'testuser' },
25+
visibility: 'Public'
26+
}
27+
},
28+
isOwner: true
29+
};
30+
31+
const subject = () => {
32+
reduxRender(
33+
<table>
34+
<tbody>
35+
<CollectionItemRow {...subjectProps} />
36+
</tbody>
37+
</table>,
38+
{ store }
39+
);
40+
};
41+
42+
describe('<CollectionItemRow />', () => {
43+
afterEach(() => {
44+
store.clearActions();
45+
});
46+
47+
it('renders the sketch name as a link', () => {
48+
subject();
49+
const link = screen.getByRole('link', { name: 'My Sketch' });
50+
expect(link).toBeInTheDocument();
51+
expect(link).toHaveAttribute('href', '/testuser/sketches/project-456');
52+
});
53+
54+
it('renders the owner username', () => {
55+
subject();
56+
expect(screen.getByText('testuser')).toBeInTheDocument();
57+
});
58+
59+
it('shows the remove button when user is the owner', () => {
60+
subject();
61+
expect(screen.getByRole('button', { name: /remove/i })).toBeInTheDocument();
62+
});
63+
64+
describe('when user is not the owner', () => {
65+
beforeAll(() => {
66+
subjectProps = { ...subjectProps, isOwner: false };
67+
});
68+
69+
afterAll(() => {
70+
subjectProps = { ...subjectProps, isOwner: true };
71+
});
72+
73+
it('does not show the remove button', () => {
74+
subject();
75+
expect(
76+
screen.queryByRole('button', { name: /remove/i })
77+
).not.toBeInTheDocument();
78+
});
79+
});
80+
81+
it('wraps the remove button with a tooltip', async () => {
82+
subject();
83+
84+
const button = screen.getByRole('button', { name: /remove/i });
85+
await act(async () => {
86+
fireEvent.mouseEnter(button);
87+
});
88+
expect(button).toHaveClass('tooltipped');
89+
});
90+
91+
describe('when the project is deleted', () => {
92+
beforeAll(() => {
93+
subjectProps = {
94+
...subjectProps,
95+
item: {
96+
...subjectProps.item,
97+
isDeleted: true,
98+
project: undefined
99+
}
100+
};
101+
});
102+
103+
it('shows the deleted sketch label', () => {
104+
subject();
105+
expect(screen.getByText('Sketch deleted')).toBeInTheDocument();
106+
});
107+
});
108+
});

client/styles/components/_collection.scss

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,22 @@
148148
.collection-row__action-column {
149149
width: #{math.div(60, $base-font-size)}rem;
150150
position: relative;
151+
152+
.tooltip-wrapper {
153+
display: inline-flex;
154+
width: auto;
155+
156+
.tooltipped::after {
157+
right: 0;
158+
left: auto;
159+
}
160+
161+
.tooltipped-n::before,
162+
.tooltipped::before {
163+
right: #{math.div(10, $base-font-size)}rem;
164+
left: auto;
165+
}
166+
}
151167
}
152168

153169
.collection-row__remove-button {

0 commit comments

Comments
 (0)