@object-ui/plugin-detail 3.1.5 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +21 -1
- package/dist/AddressField-LgHnO2Lk.js +98 -0
- package/dist/AutoNumberField-xZCrU0eW.js +14 -0
- package/dist/{AvatarField-YGj51ozd.js → AvatarField-Dy2XGlPz.js} +16 -15
- package/dist/{BooleanField-CaA898Tk.js → BooleanField-C0Clfka5.js} +11 -10
- package/dist/CodeField-CHUa07B6.js +23 -0
- package/dist/ColorField-vxHqEhcS.js +38 -0
- package/dist/CurrencyField-DiWjYWDo.js +49 -0
- package/dist/DateField-DGaRPM4P.js +22 -0
- package/dist/DateTimeField-8QnpsI_h.js +30 -0
- package/dist/EmailField-CkVgMbpI.js +26 -0
- package/dist/FileField-5UPV7uek.js +149 -0
- package/dist/FormulaField-BUgt6-Pi.js +17 -0
- package/dist/GeolocationField-D9T_jgG6.js +118 -0
- package/dist/GridField-DE_HwiIN.js +49 -0
- package/dist/ImageField-Dswnqtzf.js +73 -0
- package/dist/LocationField-gjqbE6na.js +36 -0
- package/dist/LookupField-BcS3LRKc.js +901 -0
- package/dist/{MasterDetailField-I1A9oEGC.js → MasterDetailField-BF6_-X3A.js} +20 -19
- package/dist/NumberField-Dj2rYmrS.js +27 -0
- package/dist/ObjectField-BymIojwd.js +50 -0
- package/dist/{PasswordField-DBtluGJ1.js → PasswordField-ED_Xgqz-.js} +8 -7
- package/dist/PercentField-D-JKOxKC.js +61 -0
- package/dist/PhoneField-DSCaGYq7.js +26 -0
- package/dist/QRCodeField-CtcOUapi.js +73 -0
- package/dist/{RatingField-B_Mnr63i.js → RatingField-BDnyQFWy.js} +10 -9
- package/dist/RichTextField-CH6LVZQA.js +33 -0
- package/dist/SelectField-DE4dpkMV.js +36 -0
- package/dist/{SignatureField-CddhEK9u.js → SignatureField-B1wh3f5A.js} +18 -17
- package/dist/{SliderField-Df5hMzNc.js → SliderField-zoTCKh9n.js} +2 -1
- package/dist/SummaryField-BeBVT6VN.js +22 -0
- package/dist/TextAreaField-rfUGrRxh.js +37 -0
- package/dist/TextField-C_yM7ATQ.js +30 -0
- package/dist/TimeField-BcQmBZi9.js +22 -0
- package/dist/UrlField-BakaF6NI.js +31 -0
- package/dist/UserField-zS7y3eKb.js +76 -0
- package/dist/VectorField-CTZ4myDM.js +34 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1741 -1504
- package/dist/index.umd.cjs +43 -51
- package/dist/packages/plugin-detail/src/ActivityTimeline.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/CommentAttachment.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/CommentInput.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/DetailSection.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/DetailTabs.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/DetailView.d.ts +47 -0
- package/dist/packages/plugin-detail/src/DetailView.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/DetailView.stories.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/DiffView.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/FieldChangeItem.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/HeaderHighlight.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/InlineCreateRelated.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/MentionAutocomplete.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/PointInTimeRestore.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/ReactionPicker.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/RecordActivityTimeline.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/RecordChatterPanel.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/RecordComments.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/RecordNavigationEnhanced.d.ts.map +1 -0
- package/dist/{src → packages/plugin-detail/src}/RelatedList.d.ts +8 -0
- package/dist/packages/plugin-detail/src/RelatedList.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/RelationshipGraph.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/RichTextCommentInput.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/SectionGroup.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/SubscriptionToggle.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/ThreadedReplies.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/autoLayout.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/index.d.ts.map +1 -0
- package/dist/packages/plugin-detail/src/useDetailTranslation.d.ts.map +1 -0
- package/dist/plugin-detail.css +1 -2
- package/dist/rolldown-runtime-DnwLefa7.js +23 -0
- package/dist/{src-CXr1-vVl.js → src-DyUKLvMN.js} +29788 -37711
- package/dist/useFieldTranslation-BRgjC1oq.js +9 -0
- package/package.json +34 -12
- package/.turbo/turbo-build.log +0 -61
- package/dist/AddressField-DBkEyMcG.js +0 -93
- package/dist/AutoNumberField-Baa191z-.js +0 -14
- package/dist/CodeField-BU51nl1L.js +0 -22
- package/dist/ColorField-Cnf6ZM7c.js +0 -37
- package/dist/CurrencyField-Wg-XOId2.js +0 -51
- package/dist/DateField-Cth1ky_m.js +0 -21
- package/dist/DateTimeField-B0m6FhHL.js +0 -32
- package/dist/EmailField-Do7qT_L_.js +0 -28
- package/dist/FileField-aRJAdbQb.js +0 -151
- package/dist/FormulaField-DTMkagFx.js +0 -14
- package/dist/GeolocationField-RqpHWTEv.js +0 -113
- package/dist/GridField-D4IH0cpo.js +0 -51
- package/dist/ImageField-BYCFajjr.js +0 -75
- package/dist/LocationField-Bi_ew9sd.js +0 -35
- package/dist/LookupField-BjwlDPtt.js +0 -902
- package/dist/NumberField-D_NucQlp.js +0 -26
- package/dist/ObjectField-CG-LaM65.js +0 -52
- package/dist/PercentField-B6sO_J3i.js +0 -63
- package/dist/PhoneField-CcQAWwR6.js +0 -28
- package/dist/QRCodeField-CEjWs-J5.js +0 -72
- package/dist/RichTextField-qOEJl5Ai.js +0 -32
- package/dist/SelectField-C8hWu3gm.js +0 -30
- package/dist/SummaryField-DgiFm-Cr.js +0 -19
- package/dist/TextAreaField-DuriTqsD.js +0 -36
- package/dist/TextField-CGNSl7RU.js +0 -29
- package/dist/TimeField-YO58ctFg.js +0 -21
- package/dist/UrlField-1-BMM1jn.js +0 -33
- package/dist/UserField-B6GqxP_S.js +0 -78
- package/dist/VectorField-BkEjbSt0.js +0 -36
- package/dist/src/ActivityTimeline.d.ts.map +0 -1
- package/dist/src/CommentAttachment.d.ts.map +0 -1
- package/dist/src/CommentInput.d.ts.map +0 -1
- package/dist/src/DetailSection.d.ts.map +0 -1
- package/dist/src/DetailTabs.d.ts.map +0 -1
- package/dist/src/DetailView.d.ts +0 -23
- package/dist/src/DetailView.d.ts.map +0 -1
- package/dist/src/DetailView.stories.d.ts.map +0 -1
- package/dist/src/DiffView.d.ts.map +0 -1
- package/dist/src/FieldChangeItem.d.ts.map +0 -1
- package/dist/src/HeaderHighlight.d.ts.map +0 -1
- package/dist/src/InlineCreateRelated.d.ts.map +0 -1
- package/dist/src/MentionAutocomplete.d.ts.map +0 -1
- package/dist/src/PointInTimeRestore.d.ts.map +0 -1
- package/dist/src/ReactionPicker.d.ts.map +0 -1
- package/dist/src/RecordActivityTimeline.d.ts.map +0 -1
- package/dist/src/RecordChatterPanel.d.ts.map +0 -1
- package/dist/src/RecordComments.d.ts.map +0 -1
- package/dist/src/RecordNavigationEnhanced.d.ts.map +0 -1
- package/dist/src/RelatedList.d.ts.map +0 -1
- package/dist/src/RelationshipGraph.d.ts.map +0 -1
- package/dist/src/RichTextCommentInput.d.ts.map +0 -1
- package/dist/src/SectionGroup.d.ts.map +0 -1
- package/dist/src/SubscriptionToggle.d.ts.map +0 -1
- package/dist/src/ThreadedReplies.d.ts.map +0 -1
- package/dist/src/autoLayout.d.ts.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/useDetailTranslation.d.ts.map +0 -1
- package/src/ActivityTimeline.tsx +0 -184
- package/src/CommentAttachment.tsx +0 -192
- package/src/CommentInput.tsx +0 -81
- package/src/DetailSection.tsx +0 -340
- package/src/DetailTabs.tsx +0 -73
- package/src/DetailView.stories.tsx +0 -334
- package/src/DetailView.tsx +0 -823
- package/src/DiffView.tsx +0 -231
- package/src/FieldChangeItem.tsx +0 -46
- package/src/HeaderHighlight.tsx +0 -88
- package/src/InlineCreateRelated.tsx +0 -291
- package/src/MentionAutocomplete.tsx +0 -123
- package/src/PointInTimeRestore.tsx +0 -261
- package/src/ReactionPicker.tsx +0 -106
- package/src/RecordActivityTimeline.tsx +0 -429
- package/src/RecordChatterPanel.tsx +0 -207
- package/src/RecordComments.tsx +0 -215
- package/src/RecordNavigationEnhanced.tsx +0 -211
- package/src/RelatedList.tsx +0 -413
- package/src/RelationshipGraph.tsx +0 -286
- package/src/RichTextCommentInput.tsx +0 -348
- package/src/SectionGroup.tsx +0 -101
- package/src/SubscriptionToggle.tsx +0 -60
- package/src/ThreadedReplies.tsx +0 -161
- package/src/__tests__/ActivityTimeline.test.tsx +0 -119
- package/src/__tests__/ActivityTimelineFiltering.test.tsx +0 -143
- package/src/__tests__/CommentInput.test.tsx +0 -57
- package/src/__tests__/DetailSection.test.tsx +0 -490
- package/src/__tests__/DetailView.test.tsx +0 -694
- package/src/__tests__/FieldChangeItem.test.tsx +0 -119
- package/src/__tests__/HeaderHighlight.test.tsx +0 -213
- package/src/__tests__/MentionAutocomplete.test.tsx +0 -97
- package/src/__tests__/ReactionPicker.test.tsx +0 -113
- package/src/__tests__/RecordActivityTimeline.test.tsx +0 -395
- package/src/__tests__/RecordChatterPanel.test.tsx +0 -265
- package/src/__tests__/RecordComments.test.tsx +0 -96
- package/src/__tests__/RecordCommentsPinSearch.test.tsx +0 -133
- package/src/__tests__/RelatedList.test.tsx +0 -160
- package/src/__tests__/SectionGroup.test.tsx +0 -101
- package/src/__tests__/SubscriptionToggle.test.tsx +0 -84
- package/src/__tests__/ThreadedReplies.test.tsx +0 -212
- package/src/__tests__/autoLayout.test.ts +0 -228
- package/src/__tests__/phase12-features.test.tsx +0 -583
- package/src/__tests__/roadmap-features.test.tsx +0 -478
- package/src/autoLayout.ts +0 -128
- package/src/index.tsx +0 -149
- package/src/useDetailTranslation.ts +0 -114
- package/tsconfig.json +0 -18
- package/vite.config.ts +0 -56
- package/vitest.config.ts +0 -13
- package/vitest.setup.ts +0 -1
- /package/dist/{src → packages/plugin-detail/src}/ActivityTimeline.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/CommentAttachment.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/CommentInput.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/DetailSection.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/DetailTabs.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/DetailView.stories.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/DiffView.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/FieldChangeItem.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/HeaderHighlight.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/InlineCreateRelated.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/MentionAutocomplete.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/PointInTimeRestore.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/ReactionPicker.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/RecordActivityTimeline.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/RecordChatterPanel.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/RecordComments.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/RecordNavigationEnhanced.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/RelationshipGraph.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/RichTextCommentInput.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/SectionGroup.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/SubscriptionToggle.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/ThreadedReplies.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/autoLayout.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/index.d.ts +0 -0
- /package/dist/{src → packages/plugin-detail/src}/useDetailTranslation.d.ts +0 -0
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
10
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
11
|
-
import '@testing-library/jest-dom';
|
|
12
|
-
import { RecordActivityTimeline } from '../RecordActivityTimeline';
|
|
13
|
-
import type { FeedItem } from '@object-ui/types';
|
|
14
|
-
|
|
15
|
-
const mockItems: FeedItem[] = [
|
|
16
|
-
{
|
|
17
|
-
id: '1',
|
|
18
|
-
type: 'comment',
|
|
19
|
-
actor: 'Alice',
|
|
20
|
-
body: 'This looks great!',
|
|
21
|
-
createdAt: '2026-02-20T10:00:00Z',
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
id: '2',
|
|
25
|
-
type: 'field_change',
|
|
26
|
-
actor: 'Bob',
|
|
27
|
-
createdAt: '2026-02-20T11:00:00Z',
|
|
28
|
-
fieldChanges: [
|
|
29
|
-
{
|
|
30
|
-
field: 'status',
|
|
31
|
-
fieldLabel: 'Status',
|
|
32
|
-
oldDisplayValue: 'Open',
|
|
33
|
-
newDisplayValue: 'In Progress',
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
id: '3',
|
|
39
|
-
type: 'task',
|
|
40
|
-
actor: 'Charlie',
|
|
41
|
-
body: 'Follow up with client',
|
|
42
|
-
createdAt: '2026-02-20T12:00:00Z',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
id: '4',
|
|
46
|
-
type: 'system',
|
|
47
|
-
actor: 'System',
|
|
48
|
-
body: 'Record created automatically',
|
|
49
|
-
createdAt: '2026-02-20T08:00:00Z',
|
|
50
|
-
},
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
const allTypeItems: FeedItem[] = [
|
|
54
|
-
{ id: 'c1', type: 'comment', actor: 'A', body: 'comment', createdAt: '2026-02-20T10:00:00Z' },
|
|
55
|
-
{ id: 'fc1', type: 'field_change', actor: 'B', createdAt: '2026-02-20T10:01:00Z', fieldChanges: [{ field: 'x', oldValue: '1', newValue: '2' }] },
|
|
56
|
-
{ id: 't1', type: 'task', actor: 'C', body: 'task', createdAt: '2026-02-20T10:02:00Z' },
|
|
57
|
-
{ id: 'e1', type: 'event', actor: 'D', body: 'event', createdAt: '2026-02-20T10:03:00Z' },
|
|
58
|
-
{ id: 's1', type: 'system', actor: 'E', body: 'system', createdAt: '2026-02-20T10:04:00Z' },
|
|
59
|
-
{ id: 'm1', type: 'email', actor: 'F', body: 'email', createdAt: '2026-02-20T10:05:00Z' },
|
|
60
|
-
{ id: 'p1', type: 'call', actor: 'G', body: 'call', createdAt: '2026-02-20T10:06:00Z' },
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
describe('RecordActivityTimeline', () => {
|
|
64
|
-
it('should render activity heading with count', () => {
|
|
65
|
-
render(<RecordActivityTimeline items={mockItems} />);
|
|
66
|
-
expect(screen.getByText('Activity')).toBeInTheDocument();
|
|
67
|
-
expect(screen.getByText('(4)')).toBeInTheDocument();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should render actor names', () => {
|
|
71
|
-
render(<RecordActivityTimeline items={mockItems} />);
|
|
72
|
-
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
73
|
-
expect(screen.getByText('Bob')).toBeInTheDocument();
|
|
74
|
-
expect(screen.getByText('Charlie')).toBeInTheDocument();
|
|
75
|
-
expect(screen.getByText('System')).toBeInTheDocument();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should render comment body text', () => {
|
|
79
|
-
render(<RecordActivityTimeline items={mockItems} />);
|
|
80
|
-
expect(screen.getByText('This looks great!')).toBeInTheDocument();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should render field change entries', () => {
|
|
84
|
-
render(<RecordActivityTimeline items={mockItems} />);
|
|
85
|
-
expect(screen.getByText('Status')).toBeInTheDocument();
|
|
86
|
-
expect(screen.getByText('Open')).toBeInTheDocument();
|
|
87
|
-
expect(screen.getByText('In Progress')).toBeInTheDocument();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should show "No activity recorded" when empty', () => {
|
|
91
|
-
render(<RecordActivityTimeline items={[]} />);
|
|
92
|
-
expect(screen.getByText('No activity recorded')).toBeInTheDocument();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should not show "No activity recorded" when collapseWhenEmpty is true', () => {
|
|
96
|
-
render(<RecordActivityTimeline items={[]} collapseWhenEmpty />);
|
|
97
|
-
expect(screen.queryByText('No activity recorded')).not.toBeInTheDocument();
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should filter to comments only', () => {
|
|
101
|
-
render(
|
|
102
|
-
<RecordActivityTimeline items={mockItems} filterMode="comments_only" />,
|
|
103
|
-
);
|
|
104
|
-
expect(screen.getByText('(1)')).toBeInTheDocument();
|
|
105
|
-
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
106
|
-
expect(screen.queryByText('Bob')).not.toBeInTheDocument();
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should filter to changes only', () => {
|
|
110
|
-
render(
|
|
111
|
-
<RecordActivityTimeline items={mockItems} filterMode="changes_only" />,
|
|
112
|
-
);
|
|
113
|
-
expect(screen.getByText('(1)')).toBeInTheDocument();
|
|
114
|
-
expect(screen.getByText('Bob')).toBeInTheDocument();
|
|
115
|
-
expect(screen.queryByText('Alice')).not.toBeInTheDocument();
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('should filter to tasks only', () => {
|
|
119
|
-
render(
|
|
120
|
-
<RecordActivityTimeline items={mockItems} filterMode="tasks_only" />,
|
|
121
|
-
);
|
|
122
|
-
expect(screen.getByText('(1)')).toBeInTheDocument();
|
|
123
|
-
expect(screen.getByText('Charlie')).toBeInTheDocument();
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should show filter dropdown by default', () => {
|
|
127
|
-
render(<RecordActivityTimeline items={mockItems} />);
|
|
128
|
-
expect(screen.getByLabelText('Filter activity')).toBeInTheDocument();
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should hide filter when showFilterToggle is false', () => {
|
|
132
|
-
render(
|
|
133
|
-
<RecordActivityTimeline
|
|
134
|
-
items={mockItems}
|
|
135
|
-
config={{ showFilterToggle: false }}
|
|
136
|
-
/>,
|
|
137
|
-
);
|
|
138
|
-
expect(screen.queryByLabelText('Filter activity')).not.toBeInTheDocument();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should show comment input when onAddComment provided', () => {
|
|
142
|
-
const onAdd = vi.fn();
|
|
143
|
-
render(
|
|
144
|
-
<RecordActivityTimeline items={[]} onAddComment={onAdd} />,
|
|
145
|
-
);
|
|
146
|
-
expect(screen.getByPlaceholderText(/Leave a comment/)).toBeInTheDocument();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should hide comment input when showCommentInput is false', () => {
|
|
150
|
-
const onAdd = vi.fn();
|
|
151
|
-
render(
|
|
152
|
-
<RecordActivityTimeline
|
|
153
|
-
items={[]}
|
|
154
|
-
onAddComment={onAdd}
|
|
155
|
-
config={{ showCommentInput: false }}
|
|
156
|
-
/>,
|
|
157
|
-
);
|
|
158
|
-
expect(screen.queryByPlaceholderText(/Leave a comment/)).not.toBeInTheDocument();
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('should call onAddComment when comment is submitted', () => {
|
|
162
|
-
const onAdd = vi.fn().mockResolvedValue(undefined);
|
|
163
|
-
render(
|
|
164
|
-
<RecordActivityTimeline items={[]} onAddComment={onAdd} />,
|
|
165
|
-
);
|
|
166
|
-
fireEvent.change(screen.getByPlaceholderText(/Leave a comment/), {
|
|
167
|
-
target: { value: 'New comment' },
|
|
168
|
-
});
|
|
169
|
-
fireEvent.click(screen.getByLabelText('Submit comment'));
|
|
170
|
-
expect(onAdd).toHaveBeenCalledWith('New comment');
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('should show Load more button when hasMore is true', () => {
|
|
174
|
-
render(
|
|
175
|
-
<RecordActivityTimeline items={mockItems} hasMore onLoadMore={() => {}} />,
|
|
176
|
-
);
|
|
177
|
-
expect(screen.getByLabelText('Load more activity')).toBeInTheDocument();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should call onLoadMore when Load more is clicked', () => {
|
|
181
|
-
const onLoadMore = vi.fn().mockResolvedValue(undefined);
|
|
182
|
-
render(
|
|
183
|
-
<RecordActivityTimeline items={mockItems} hasMore onLoadMore={onLoadMore} />,
|
|
184
|
-
);
|
|
185
|
-
fireEvent.click(screen.getByLabelText('Load more activity'));
|
|
186
|
-
expect(onLoadMore).toHaveBeenCalled();
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('should render source label when present', () => {
|
|
190
|
-
const items: FeedItem[] = [
|
|
191
|
-
{ id: '1', type: 'comment', actor: 'Alice', body: 'Hi', createdAt: '2026-02-20T10:00:00Z', source: 'email' },
|
|
192
|
-
];
|
|
193
|
-
render(<RecordActivityTimeline items={items} />);
|
|
194
|
-
expect(screen.getByText('via email')).toBeInTheDocument();
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('should render edited indicator', () => {
|
|
198
|
-
const items: FeedItem[] = [
|
|
199
|
-
{ id: '1', type: 'comment', actor: 'Alice', body: 'Edited', createdAt: '2026-02-20T10:00:00Z', edited: true },
|
|
200
|
-
];
|
|
201
|
-
render(<RecordActivityTimeline items={items} />);
|
|
202
|
-
expect(screen.getByText('(edited)')).toBeInTheDocument();
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it('should render pinned indicator', () => {
|
|
206
|
-
const items: FeedItem[] = [
|
|
207
|
-
{ id: '1', type: 'comment', actor: 'Alice', body: 'Pinned comment', createdAt: '2026-02-20T10:00:00Z', pinned: true },
|
|
208
|
-
];
|
|
209
|
-
render(<RecordActivityTimeline items={items} />);
|
|
210
|
-
expect(screen.getByText('📌 Pinned')).toBeInTheDocument();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('should show subscription toggle when configured', () => {
|
|
214
|
-
render(
|
|
215
|
-
<RecordActivityTimeline
|
|
216
|
-
items={[]}
|
|
217
|
-
config={{ showSubscriptionToggle: true }}
|
|
218
|
-
subscription={{ recordId: '1', subscribed: true }}
|
|
219
|
-
onToggleSubscription={() => {}}
|
|
220
|
-
/>,
|
|
221
|
-
);
|
|
222
|
-
expect(screen.getByLabelText('Unsubscribe from notifications')).toBeInTheDocument();
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('should render all 7 item types', () => {
|
|
226
|
-
render(<RecordActivityTimeline items={allTypeItems} />);
|
|
227
|
-
expect(screen.getByText('A')).toBeInTheDocument();
|
|
228
|
-
expect(screen.getByText('B')).toBeInTheDocument();
|
|
229
|
-
expect(screen.getByText('C')).toBeInTheDocument();
|
|
230
|
-
expect(screen.getByText('D')).toBeInTheDocument();
|
|
231
|
-
expect(screen.getByText('E')).toBeInTheDocument();
|
|
232
|
-
expect(screen.getByText('F')).toBeInTheDocument();
|
|
233
|
-
expect(screen.getByText('G')).toBeInTheDocument();
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('should render actor avatar when actorAvatarUrl is provided', () => {
|
|
237
|
-
const items: FeedItem[] = [
|
|
238
|
-
{ id: '1', type: 'comment', actor: 'Alice', actorAvatarUrl: 'https://example.com/alice.png', body: 'Hi', createdAt: '2026-02-20T10:00:00Z' },
|
|
239
|
-
];
|
|
240
|
-
render(<RecordActivityTimeline items={items} />);
|
|
241
|
-
const img = screen.getByAltText('Alice');
|
|
242
|
-
expect(img).toBeInTheDocument();
|
|
243
|
-
expect(img).toHaveAttribute('src', 'https://example.com/alice.png');
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('should submit comment via Ctrl+Enter', () => {
|
|
247
|
-
const onAdd = vi.fn().mockResolvedValue(undefined);
|
|
248
|
-
render(
|
|
249
|
-
<RecordActivityTimeline items={[]} onAddComment={onAdd} />,
|
|
250
|
-
);
|
|
251
|
-
const textarea = screen.getByPlaceholderText(/Leave a comment/);
|
|
252
|
-
fireEvent.change(textarea, { target: { value: 'Ctrl enter test' } });
|
|
253
|
-
fireEvent.keyDown(textarea, { key: 'Enter', ctrlKey: true });
|
|
254
|
-
expect(onAdd).toHaveBeenCalledWith('Ctrl enter test');
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('should clear input after successful comment submission', async () => {
|
|
258
|
-
const onAdd = vi.fn().mockResolvedValue(undefined);
|
|
259
|
-
render(
|
|
260
|
-
<RecordActivityTimeline items={[]} onAddComment={onAdd} />,
|
|
261
|
-
);
|
|
262
|
-
const textarea = screen.getByPlaceholderText(/Leave a comment/) as HTMLTextAreaElement;
|
|
263
|
-
fireEvent.change(textarea, { target: { value: 'Will be cleared' } });
|
|
264
|
-
fireEvent.click(screen.getByLabelText('Submit comment'));
|
|
265
|
-
await vi.waitFor(() => {
|
|
266
|
-
expect(textarea.value).toBe('');
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('should disable input and button during submission', async () => {
|
|
271
|
-
let resolveSubmit: () => void;
|
|
272
|
-
const onAdd = vi.fn(() => new Promise<void>((r) => { resolveSubmit = r; }));
|
|
273
|
-
render(
|
|
274
|
-
<RecordActivityTimeline items={[]} onAddComment={onAdd} />,
|
|
275
|
-
);
|
|
276
|
-
const textarea = screen.getByPlaceholderText(/Leave a comment/) as HTMLTextAreaElement;
|
|
277
|
-
fireEvent.change(textarea, { target: { value: 'submitting' } });
|
|
278
|
-
fireEvent.click(screen.getByLabelText('Submit comment'));
|
|
279
|
-
expect(textarea).toBeDisabled();
|
|
280
|
-
expect(screen.getByLabelText('Submit comment')).toBeDisabled();
|
|
281
|
-
resolveSubmit!();
|
|
282
|
-
await vi.waitFor(() => {
|
|
283
|
-
expect(textarea).not.toBeDisabled();
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('should show loading spinner when loading more', async () => {
|
|
288
|
-
let resolveLoad: () => void;
|
|
289
|
-
const onLoadMore = vi.fn(() => new Promise<void>((r) => { resolveLoad = r; }));
|
|
290
|
-
render(
|
|
291
|
-
<RecordActivityTimeline items={mockItems} hasMore onLoadMore={onLoadMore} />,
|
|
292
|
-
);
|
|
293
|
-
fireEvent.click(screen.getByLabelText('Load more activity'));
|
|
294
|
-
// Loader2 spinner should be present while loading
|
|
295
|
-
expect(screen.getByLabelText('Load more activity')).toBeDisabled();
|
|
296
|
-
resolveLoad!();
|
|
297
|
-
await vi.waitFor(() => {
|
|
298
|
-
expect(screen.getByLabelText('Load more activity')).not.toBeDisabled();
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it('should use controlled filterMode and call onFilterChange', () => {
|
|
303
|
-
const onFilterChange = vi.fn();
|
|
304
|
-
render(
|
|
305
|
-
<RecordActivityTimeline
|
|
306
|
-
items={mockItems}
|
|
307
|
-
filterMode="comments_only"
|
|
308
|
-
onFilterChange={onFilterChange}
|
|
309
|
-
/>,
|
|
310
|
-
);
|
|
311
|
-
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
312
|
-
expect(screen.queryByText('Bob')).not.toBeInTheDocument();
|
|
313
|
-
// Change filter via select
|
|
314
|
-
fireEvent.change(screen.getByLabelText('Filter activity'), { target: { value: 'all' } });
|
|
315
|
-
expect(onFilterChange).toHaveBeenCalledWith('all');
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it('should use internal filter state when no controlled filterMode', () => {
|
|
319
|
-
render(
|
|
320
|
-
<RecordActivityTimeline items={mockItems} />,
|
|
321
|
-
);
|
|
322
|
-
// Initially all visible
|
|
323
|
-
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
324
|
-
expect(screen.getByText('Bob')).toBeInTheDocument();
|
|
325
|
-
// Switch to comments_only
|
|
326
|
-
fireEvent.change(screen.getByLabelText('Filter activity'), { target: { value: 'comments_only' } });
|
|
327
|
-
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
328
|
-
expect(screen.queryByText('Bob')).not.toBeInTheDocument();
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it('should not render comment input when onAddComment is not provided', () => {
|
|
332
|
-
render(<RecordActivityTimeline items={[]} />);
|
|
333
|
-
expect(screen.queryByPlaceholderText(/Leave a comment/)).not.toBeInTheDocument();
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
it('should render reactions when enableReactions is true', () => {
|
|
337
|
-
const items: FeedItem[] = [
|
|
338
|
-
{
|
|
339
|
-
id: '1',
|
|
340
|
-
type: 'comment',
|
|
341
|
-
actor: 'Alice',
|
|
342
|
-
body: 'Nice!',
|
|
343
|
-
createdAt: '2026-02-20T10:00:00Z',
|
|
344
|
-
reactions: [{ emoji: '👍', count: 2, reacted: true }],
|
|
345
|
-
},
|
|
346
|
-
];
|
|
347
|
-
render(
|
|
348
|
-
<RecordActivityTimeline items={items} config={{ enableReactions: true }} />,
|
|
349
|
-
);
|
|
350
|
-
expect(screen.getByText('👍')).toBeInTheDocument();
|
|
351
|
-
expect(screen.getByText('2')).toBeInTheDocument();
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
it('should not render reactions when enableReactions is false', () => {
|
|
355
|
-
const items: FeedItem[] = [
|
|
356
|
-
{
|
|
357
|
-
id: '1',
|
|
358
|
-
type: 'comment',
|
|
359
|
-
actor: 'Alice',
|
|
360
|
-
body: 'Nice!',
|
|
361
|
-
createdAt: '2026-02-20T10:00:00Z',
|
|
362
|
-
reactions: [{ emoji: '👍', count: 2, reacted: true }],
|
|
363
|
-
},
|
|
364
|
-
];
|
|
365
|
-
render(
|
|
366
|
-
<RecordActivityTimeline items={items} config={{ enableReactions: false }} />,
|
|
367
|
-
);
|
|
368
|
-
expect(screen.queryByText('👍')).not.toBeInTheDocument();
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it('should group items by parentId when enableThreading is true', () => {
|
|
372
|
-
const items: FeedItem[] = [
|
|
373
|
-
{ id: 'p1', type: 'comment', actor: 'Alice', body: 'Root comment', createdAt: '2026-02-20T10:00:00Z', replyCount: 1 },
|
|
374
|
-
{ id: 'r1', type: 'comment', actor: 'Bob', body: 'Reply', createdAt: '2026-02-20T11:00:00Z', parentId: 'p1' },
|
|
375
|
-
];
|
|
376
|
-
render(
|
|
377
|
-
<RecordActivityTimeline items={items} config={{ enableThreading: true }} />,
|
|
378
|
-
);
|
|
379
|
-
// Root comment rendered
|
|
380
|
-
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
381
|
-
expect(screen.getByText('Root comment')).toBeInTheDocument();
|
|
382
|
-
// Reply is shown as threaded reply (collapsed)
|
|
383
|
-
expect(screen.getByText('1 reply')).toBeInTheDocument();
|
|
384
|
-
// Reply actor should not be directly visible (collapsed in ThreadedReplies)
|
|
385
|
-
expect(screen.queryByText('Bob')).not.toBeInTheDocument();
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
it('should show "via {source}" label', () => {
|
|
389
|
-
const items: FeedItem[] = [
|
|
390
|
-
{ id: '1', type: 'comment', actor: 'Alice', body: 'Hi', createdAt: '2026-02-20T10:00:00Z', source: 'slack' },
|
|
391
|
-
];
|
|
392
|
-
render(<RecordActivityTimeline items={items} />);
|
|
393
|
-
expect(screen.getByText('via slack')).toBeInTheDocument();
|
|
394
|
-
});
|
|
395
|
-
});
|
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
10
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
11
|
-
import '@testing-library/jest-dom';
|
|
12
|
-
import { RecordChatterPanel } from '../RecordChatterPanel';
|
|
13
|
-
import type { FeedItem } from '@object-ui/types';
|
|
14
|
-
|
|
15
|
-
const mockItems: FeedItem[] = [
|
|
16
|
-
{
|
|
17
|
-
id: '1',
|
|
18
|
-
type: 'comment',
|
|
19
|
-
actor: 'Alice',
|
|
20
|
-
body: 'Hello from chatter',
|
|
21
|
-
createdAt: '2026-02-20T10:00:00Z',
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
id: '2',
|
|
25
|
-
type: 'field_change',
|
|
26
|
-
actor: 'Bob',
|
|
27
|
-
createdAt: '2026-02-20T11:00:00Z',
|
|
28
|
-
fieldChanges: [
|
|
29
|
-
{ field: 'priority', fieldLabel: 'Priority', oldValue: 'low', newValue: 'high' },
|
|
30
|
-
],
|
|
31
|
-
},
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
describe('RecordChatterPanel', () => {
|
|
35
|
-
describe('sidebar mode (right)', () => {
|
|
36
|
-
it('should render Discussion header in sidebar mode', () => {
|
|
37
|
-
render(
|
|
38
|
-
<RecordChatterPanel config={{ position: 'right' }} items={mockItems} />,
|
|
39
|
-
);
|
|
40
|
-
expect(screen.getByText('Discussion')).toBeInTheDocument();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should render activity items', () => {
|
|
44
|
-
render(
|
|
45
|
-
<RecordChatterPanel config={{ position: 'right' }} items={mockItems} />,
|
|
46
|
-
);
|
|
47
|
-
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
48
|
-
expect(screen.getByText('Hello from chatter')).toBeInTheDocument();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should collapse when close button is clicked', () => {
|
|
52
|
-
render(
|
|
53
|
-
<RecordChatterPanel
|
|
54
|
-
config={{ position: 'right', collapsible: true }}
|
|
55
|
-
items={mockItems}
|
|
56
|
-
/>,
|
|
57
|
-
);
|
|
58
|
-
// Initially expanded (not defaultCollapsed)
|
|
59
|
-
expect(screen.getByText('Discussion')).toBeInTheDocument();
|
|
60
|
-
fireEvent.click(screen.getByLabelText('Close discussion panel'));
|
|
61
|
-
// Now collapsed — show expand button
|
|
62
|
-
expect(screen.getByLabelText('Open discussion panel')).toBeInTheDocument();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should start collapsed when defaultCollapsed is true', () => {
|
|
66
|
-
render(
|
|
67
|
-
<RecordChatterPanel
|
|
68
|
-
config={{ position: 'right', collapsible: true, defaultCollapsed: true }}
|
|
69
|
-
items={mockItems}
|
|
70
|
-
/>,
|
|
71
|
-
);
|
|
72
|
-
expect(screen.getByLabelText('Open discussion panel')).toBeInTheDocument();
|
|
73
|
-
expect(screen.queryByText('Discussion')).not.toBeInTheDocument();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should expand from collapsed state', () => {
|
|
77
|
-
render(
|
|
78
|
-
<RecordChatterPanel
|
|
79
|
-
config={{ position: 'right', collapsible: true, defaultCollapsed: true }}
|
|
80
|
-
items={mockItems}
|
|
81
|
-
/>,
|
|
82
|
-
);
|
|
83
|
-
fireEvent.click(screen.getByLabelText('Open discussion panel'));
|
|
84
|
-
expect(screen.getByText('Discussion')).toBeInTheDocument();
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('inline mode (bottom)', () => {
|
|
89
|
-
it('should render timeline in inline mode', () => {
|
|
90
|
-
render(
|
|
91
|
-
<RecordChatterPanel
|
|
92
|
-
config={{ position: 'bottom', collapsible: false }}
|
|
93
|
-
items={mockItems}
|
|
94
|
-
/>,
|
|
95
|
-
);
|
|
96
|
-
expect(screen.getByText('Activity')).toBeInTheDocument();
|
|
97
|
-
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should show/hide discussion toggle in inline collapsible mode', () => {
|
|
101
|
-
render(
|
|
102
|
-
<RecordChatterPanel
|
|
103
|
-
config={{ position: 'bottom', collapsible: true, defaultCollapsed: true }}
|
|
104
|
-
items={mockItems}
|
|
105
|
-
/>,
|
|
106
|
-
);
|
|
107
|
-
expect(screen.getByLabelText('Show discussion')).toBeInTheDocument();
|
|
108
|
-
fireEvent.click(screen.getByLabelText('Show discussion'));
|
|
109
|
-
expect(screen.getByText('Activity')).toBeInTheDocument();
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
describe('default config', () => {
|
|
114
|
-
it('should default to right position', () => {
|
|
115
|
-
render(<RecordChatterPanel items={mockItems} />);
|
|
116
|
-
expect(screen.getByText('Discussion')).toBeInTheDocument();
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should pass feed config to embedded timeline', () => {
|
|
120
|
-
render(
|
|
121
|
-
<RecordChatterPanel
|
|
122
|
-
config={{ feed: { showFilterToggle: false } }}
|
|
123
|
-
items={mockItems}
|
|
124
|
-
/>,
|
|
125
|
-
);
|
|
126
|
-
expect(screen.queryByLabelText('Filter activity')).not.toBeInTheDocument();
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
describe('left sidebar mode', () => {
|
|
131
|
-
it('should render with border-r in left position', () => {
|
|
132
|
-
const { container } = render(
|
|
133
|
-
<RecordChatterPanel config={{ position: 'left' }} items={mockItems} />,
|
|
134
|
-
);
|
|
135
|
-
const panel = container.firstChild as HTMLElement;
|
|
136
|
-
expect(panel).toHaveClass('border-r');
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
describe('right sidebar width', () => {
|
|
141
|
-
it('should apply configured width via style', () => {
|
|
142
|
-
const { container } = render(
|
|
143
|
-
<RecordChatterPanel config={{ position: 'right', width: '400px' }} items={mockItems} />,
|
|
144
|
-
);
|
|
145
|
-
const panel = container.firstChild as HTMLElement;
|
|
146
|
-
expect(panel.style.width).toBe('400px');
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe('collapsible=false', () => {
|
|
151
|
-
it('should not show collapse button when collapsible is false', () => {
|
|
152
|
-
render(
|
|
153
|
-
<RecordChatterPanel
|
|
154
|
-
config={{ position: 'right', collapsible: false }}
|
|
155
|
-
items={mockItems}
|
|
156
|
-
/>,
|
|
157
|
-
);
|
|
158
|
-
expect(screen.getByText('Discussion')).toBeInTheDocument();
|
|
159
|
-
expect(screen.queryByLabelText('Close discussion panel')).not.toBeInTheDocument();
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
describe('sidebar timeline styling', () => {
|
|
164
|
-
it('should pass border-0 shadow-none to embedded timeline', () => {
|
|
165
|
-
const { container } = render(
|
|
166
|
-
<RecordChatterPanel config={{ position: 'right' }} items={mockItems} />,
|
|
167
|
-
);
|
|
168
|
-
// The RecordActivityTimeline renders a Card; in sidebar mode it gets border-0 shadow-none
|
|
169
|
-
const card = container.querySelector('.border-0.shadow-none');
|
|
170
|
-
expect(card).toBeInTheDocument();
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe('callback passthrough', () => {
|
|
175
|
-
it('should forward onAddComment to embedded timeline', () => {
|
|
176
|
-
const onAddComment = vi.fn().mockResolvedValue(undefined);
|
|
177
|
-
render(
|
|
178
|
-
<RecordChatterPanel
|
|
179
|
-
config={{ position: 'right' }}
|
|
180
|
-
items={[]}
|
|
181
|
-
onAddComment={onAddComment}
|
|
182
|
-
/>,
|
|
183
|
-
);
|
|
184
|
-
expect(screen.getByPlaceholderText(/Leave a comment/)).toBeInTheDocument();
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
describe('inline collapsible buttons', () => {
|
|
189
|
-
it('should show "Show Discussion (N)" when collapsed inline', () => {
|
|
190
|
-
render(
|
|
191
|
-
<RecordChatterPanel
|
|
192
|
-
config={{ position: 'bottom', collapsible: true, defaultCollapsed: true }}
|
|
193
|
-
items={mockItems}
|
|
194
|
-
/>,
|
|
195
|
-
);
|
|
196
|
-
expect(screen.getByText('Show Discussion (2)')).toBeInTheDocument();
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should show "Hide discussion" button when expanded inline', () => {
|
|
200
|
-
render(
|
|
201
|
-
<RecordChatterPanel
|
|
202
|
-
config={{ position: 'bottom', collapsible: true }}
|
|
203
|
-
items={mockItems}
|
|
204
|
-
/>,
|
|
205
|
-
);
|
|
206
|
-
expect(screen.getByLabelText('Hide discussion')).toBeInTheDocument();
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('should toggle between collapsed and expanded inline', () => {
|
|
210
|
-
render(
|
|
211
|
-
<RecordChatterPanel
|
|
212
|
-
config={{ position: 'bottom', collapsible: true, defaultCollapsed: true }}
|
|
213
|
-
items={mockItems}
|
|
214
|
-
/>,
|
|
215
|
-
);
|
|
216
|
-
// Collapsed
|
|
217
|
-
expect(screen.getByLabelText('Show discussion')).toBeInTheDocument();
|
|
218
|
-
fireEvent.click(screen.getByLabelText('Show discussion'));
|
|
219
|
-
// Expanded
|
|
220
|
-
expect(screen.getByText('Activity')).toBeInTheDocument();
|
|
221
|
-
// Click hide
|
|
222
|
-
fireEvent.click(screen.getByLabelText('Hide discussion'));
|
|
223
|
-
// Collapsed again
|
|
224
|
-
expect(screen.getByLabelText('Show discussion')).toBeInTheDocument();
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
describe('collapseWhenEmpty', () => {
|
|
229
|
-
it('should auto-collapse when empty and collapseWhenEmpty is true (inline mode)', () => {
|
|
230
|
-
render(
|
|
231
|
-
<RecordChatterPanel
|
|
232
|
-
config={{ position: 'bottom', collapsible: true }}
|
|
233
|
-
collapseWhenEmpty
|
|
234
|
-
items={[]}
|
|
235
|
-
/>,
|
|
236
|
-
);
|
|
237
|
-
// Should be collapsed because items is empty
|
|
238
|
-
expect(screen.getByLabelText('Show discussion')).toBeInTheDocument();
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('should not auto-collapse when items exist and collapseWhenEmpty is true', () => {
|
|
242
|
-
render(
|
|
243
|
-
<RecordChatterPanel
|
|
244
|
-
config={{ position: 'bottom', collapsible: true }}
|
|
245
|
-
collapseWhenEmpty
|
|
246
|
-
items={mockItems}
|
|
247
|
-
/>,
|
|
248
|
-
);
|
|
249
|
-
// Should be expanded because there are items
|
|
250
|
-
expect(screen.getByText('Activity')).toBeInTheDocument();
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it('should auto-collapse sidebar when empty and collapseWhenEmpty is true', () => {
|
|
254
|
-
render(
|
|
255
|
-
<RecordChatterPanel
|
|
256
|
-
config={{ position: 'right', collapsible: true }}
|
|
257
|
-
collapseWhenEmpty
|
|
258
|
-
items={[]}
|
|
259
|
-
/>,
|
|
260
|
-
);
|
|
261
|
-
// Should be collapsed
|
|
262
|
-
expect(screen.getByLabelText('Open discussion panel')).toBeInTheDocument();
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
});
|