- @if (mode === 'student' || response.replies.length > 0) {
-
- }
+
}
-
+
+
+
+
+
+ account_circle
+
+
+ {{ reply.usernames }}
+
+
+ @if (showHideButton) {
+
+
+ }
+
+
+
diff --git a/src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.spec.ts b/src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.spec.ts
new file mode 100644
index 00000000000..17eccb29ef3
--- /dev/null
+++ b/src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.spec.ts
@@ -0,0 +1,251 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ClassResponseTeacherComponent } from './class-response-teacher.component';
+import { MockComponent, MockProvider } from 'ng-mocks';
+import { SaveTimeMessageComponent } from '../../../common/save-time-message/save-time-message.component';
+import { ConfigService } from '../../../services/configService';
+import { provideRouter } from '@angular/router';
+
+let fixture: ComponentFixture
;
+let component: ClassResponseTeacherComponent;
+
+describe('ClassResponseTeacherComponent', () => {
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [ClassResponseTeacherComponent, MockComponent(SaveTimeMessageComponent)],
+ providers: [MockProvider(ConfigService), provideRouter([])]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(ClassResponseTeacherComponent);
+ component = fixture.componentInstance;
+ component.response = createResponse('Hello World');
+ fixture.detectChanges();
+ });
+
+ isHidden();
+ visiblePost();
+ hiddenPost();
+ visibleReply();
+ hiddenReply();
+ hiddenParentReply();
+ hidePost();
+ showPost();
+});
+
+function createResponse(text: string = '', replies: any[] = []): any {
+ return {
+ workgroupId: 1,
+ usernames: 'Student A',
+ serverSaveTime: Date.now(),
+ studentData: { response: text, responseTextHTML: text, attachments: [] },
+ replies,
+ latestInappropriateFlagAnnotation: null
+ };
+}
+
+function createReply(text: string = ''): any {
+ return {
+ workgroupId: 2,
+ usernames: 'Student B',
+ serverSaveTime: Date.now(),
+ studentData: { response: text, responseHTML: text },
+ latestInappropriateFlagAnnotation: null
+ };
+}
+
+function hiddenAnnotation(): any {
+ return { data: { action: 'Delete' } };
+}
+
+function queryText(selector: string): string {
+ return fixture.nativeElement.querySelector(selector)?.textContent?.trim() ?? '';
+}
+
+function query(selector: string): HTMLElement | null {
+ return fixture.nativeElement.querySelector(selector);
+}
+
+function isHidden() {
+ describe('isHidden', () => {
+ it('returns false when latestInappropriateFlagAnnotation is null', () => {
+ const post = createResponse();
+ expect(component['isHidden'](post)).toBe(false);
+ });
+
+ it('returns false when latestInappropriateFlagAnnotation data is null', () => {
+ const post = createResponse();
+ post.latestInappropriateFlagAnnotation = { data: null };
+ expect(component['isHidden'](post)).toBe(false);
+ });
+
+ it('returns false when action is not Delete', () => {
+ const post = createResponse();
+ post.latestInappropriateFlagAnnotation = { data: { action: 'Flag' } };
+ expect(component['isHidden'](post)).toBe(false);
+ });
+
+ it('returns true when latestInappropriateFlagAnnotation action is Delete', () => {
+ const post = createResponse();
+ post.latestInappropriateFlagAnnotation = hiddenAnnotation();
+ expect(component['isHidden'](post)).toBe(true);
+ });
+ });
+}
+
+function visiblePost() {
+ describe('visible post', () => {
+ it('renders post content', () => {
+ expect(queryText('.post')).toBe('Hello World');
+ });
+
+ it('includes a hide button', () => {
+ const btn = query('button[mattooltip="Hide from students"]');
+ expect(btn).not.toBeNull();
+ });
+
+ it('does not wrap the post in a element', () => {
+ expect(query('details')).toBeNull();
+ });
+ });
+}
+
+function hiddenPost() {
+ describe('hidden post', () => {
+ beforeEach(() => {
+ component.response = createResponse('Hidden content');
+ component.response.latestInappropriateFlagAnnotation = hiddenAnnotation();
+ fixture.detectChanges();
+ });
+
+ it('wraps the post in a element', () => {
+ expect(query('details')).not.toBeNull();
+ });
+
+ it('shows hidden post message', () => {
+ expect(queryText('summary')).toContain('This post is hidden from students.');
+ });
+
+ it('includes a show button', () => {
+ const btn = query('button[mattooltip="Show to students"]');
+ expect(btn).not.toBeNull();
+ });
+ });
+}
+
+function visibleReply() {
+ describe('visible reply on a visible post', () => {
+ let reply: any;
+
+ beforeEach(() => {
+ reply = createReply('Reply text');
+ component.response.replies = [reply];
+ component['repliesToShow'] = [reply];
+ fixture.detectChanges();
+ });
+
+ it('shows reply content without a wrapper', () => {
+ expect(query('.comment details')).toBeNull();
+ });
+
+ it('renders the reply content', () => {
+ expect(queryText('.comment')).toContain('Reply text');
+ });
+
+ it('includes a hide button', () => {
+ const btn = query('.comment button[mattooltip="Hide from students"]');
+ expect(btn).not.toBeNull();
+ });
+ });
+}
+
+function hiddenReply() {
+ describe('hidden reply on a visible post', () => {
+ let reply: any;
+
+ beforeEach(() => {
+ reply = createReply('Hidden reply');
+ reply.latestInappropriateFlagAnnotation = hiddenAnnotation();
+ component.response.replies = [reply];
+ component['repliesToShow'] = [reply];
+ fixture.detectChanges();
+ });
+
+ it('wraps the reply in a element', () => {
+ expect(query('.comment details')).not.toBeNull();
+ });
+
+ it('shows comment hidden message', () => {
+ expect(queryText('.comment summary')).toContain('This comment is hidden from students.');
+ });
+
+ it('includes a show button', () => {
+ const btn = query('.comment button[mattooltip="Show to students"]');
+ expect(btn).not.toBeNull();
+ });
+ });
+}
+
+function hiddenParentReply() {
+ describe('hidden parent post', () => {
+ let reply: any;
+
+ beforeEach(() => {
+ reply = createReply('Reply on hidden post');
+ component.response = createResponse('Hidden post content', [reply]);
+ component.response.latestInappropriateFlagAnnotation = hiddenAnnotation();
+ component['repliesToShow'] = [reply];
+ fixture.detectChanges();
+ });
+
+ it('wraps replies in a element', () => {
+ const detailsEls = fixture.nativeElement.querySelectorAll('.comment details');
+ expect(detailsEls.length).toBeGreaterThan(0);
+ });
+
+ it('shows parent hidden message on replies', () => {
+ expect(queryText('.comment summary')).toContain(
+ 'Comment hidden because parent post is hidden.'
+ );
+ });
+
+ it('does not include a show button on replies', () => {
+ const btn = query('.comment button[mattooltip="Show to students"]');
+ expect(btn).toBeNull();
+ });
+ });
+}
+
+function hidePost() {
+ describe('hidePost', () => {
+ it('emits hidePostEvent after confirmation', () => {
+ spyOn(window, 'confirm').and.returnValue(true);
+ const spy = spyOn(component.hidePostEvent, 'emit');
+ component['hidePost'](component.response);
+ expect(spy).toHaveBeenCalledWith(component.response);
+ });
+
+ it('does not emit hidePostEvent when confirmation is cancelled', () => {
+ spyOn(window, 'confirm').and.returnValue(false);
+ const spy = spyOn(component.hidePostEvent, 'emit');
+ component['hidePost'](component.response);
+ expect(spy).not.toHaveBeenCalled();
+ });
+ });
+}
+
+function showPost() {
+ describe('showPost', () => {
+ it('emits showPostEvent after confirmation', () => {
+ spyOn(window, 'confirm').and.returnValue(true);
+ const spy = spyOn(component.showPostEvent, 'emit');
+ component['showPost'](component.response);
+ expect(spy).toHaveBeenCalledWith(component.response);
+ });
+
+ it('does not emit showPostEvent when confirmation is cancelled', () => {
+ spyOn(window, 'confirm').and.returnValue(false);
+ const spy = spyOn(component.showPostEvent, 'emit');
+ component['showPost'](component.response);
+ expect(spy).not.toHaveBeenCalled();
+ });
+ });
+}
diff --git a/src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.ts b/src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.ts
index 425ced725d7..09a372b8125 100644
--- a/src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.ts
+++ b/src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.ts
@@ -29,6 +29,35 @@ import { ClassResponse } from '../class-response/class-response.component';
],
selector: 'class-response-teacher',
styleUrl: '../class-response/class-response.component.scss',
+ styles: `
+ @reference "tailwindcss";
+
+ details:open > summary {
+ .details-closed {
+ @apply hidden;
+ }
+
+ .details-open {
+ @apply flex;
+ }
+ }
+
+ .reply-details:open {
+ @apply px-2;
+
+ > summary {
+ @apply -mx-2 mb-1 rounded-b-none;
+ }
+ }
+
+ summary {
+ @apply italic cursor-pointer px-2 flex items-center gap-2;
+
+ &::-webkit-details-marker {
+ display: none;
+ }
+ }
+ `,
templateUrl: './class-response-teacher.component.html'
})
export class ClassResponseTeacherComponent extends ClassResponse {
@@ -49,6 +78,10 @@ export class ClassResponseTeacherComponent extends ClassResponse {
}
}
+ protected isHidden(post: any): boolean {
+ return post.latestInappropriateFlagAnnotation?.data?.action === 'Delete';
+ }
+
protected showPost(componentState: any): void {
if (confirm($localize`Are you sure you want to show this content?`)) {
this.showPostEvent.emit(componentState);
diff --git a/src/assets/wise5/components/discussion/class-response/class-response.component.ts b/src/assets/wise5/components/discussion/class-response/class-response.component.ts
index d407a21fe7e..8018b6e66d3 100644
--- a/src/assets/wise5/components/discussion/class-response/class-response.component.ts
+++ b/src/assets/wise5/components/discussion/class-response/class-response.component.ts
@@ -82,6 +82,7 @@ export class ClassResponse {
}
private injectLinks(response: string): string {
+ if (response == null) return '';
return response.replace(this.urlMatcher, (match) => {
let matchUrl = match;
if (!match.startsWith('http')) {
diff --git a/src/messages.xlf b/src/messages.xlf
index 33e39bf173f..c5898133e97 100644
--- a/src/messages.xlf
+++ b/src/messages.xlf
@@ -19336,33 +19336,62 @@ Category Name:
20
-
- Hide from students
+
+ This post is hidden from students.
src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
- 29,32
+ 5,7
+
+
+
+ (tap to view)
+
+ src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
+ 6,7
+
+
+ src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
+ 96,97
+
+
+
+ (tap to hide)
+
+ src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
+ 7,10
src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
- 100,102
+ 97,99
Show to students
src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
- 42,45
+ 11,14
+
+
+ src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
+ 102,104
+
+
+
+ Hide from students
+
+ src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
+ 51,54
src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
- 125,127
+ 149,153
Post attachment
src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
- 52,55
+ 61,66
src/assets/wise5/components/discussion/class-response/class-response.component.html
@@ -19373,7 +19402,7 @@ Category Name:
Comments ()
src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
- 62,63
+ 72,73
src/assets/wise5/components/discussion/class-response/class-response.component.html
@@ -19384,32 +19413,39 @@ Category Name:
Comments ()
src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
- 66,67
+ 76,77
src/assets/wise5/components/discussion/class-response/class-response.component.html
37,38
-
- Parent post is hidden, so this comment is also hidden
+
+ Comment hidden because parent post is hidden.
+
+ src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
+ 91,94
+
+
+
+ This comment is hidden from students.
src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.html
- 113,115
+ 94,96
Are you sure you want to hide this content?
src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.ts
- 47
+ 76
Are you sure you want to show this content?
src/assets/wise5/components/discussion/class-response-teacher/class-response-teacher.component.ts
- 53
+ 86