-
Notifications
You must be signed in to change notification settings - Fork 467
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: show Block Editor Side on Traditional (#30585)
This pull request includes multiple changes to enhance the block editor functionality and improve the workflow actions in the `core-web` project. The most important changes include the addition of a new block editor sidebar component, updates to the workflow actions service, and modifications to the event handling and test cases. ### Enhancements to Block Editor Functionality: * Added a new `DotBlockEditorSidebarComponent` with its HTML, SCSS, and TypeScript files to handle the block editor sidebar functionality. This component includes methods for saving editor changes, closing the sidebar, and handling events. [[1]](diffhunk://#diff-5490d402dcc3eb8dca39a3c27a2a45b3338bea2a103206240dbc0134f6a9b4ecR1-R35) [[2]](diffhunk://#diff-bd12ae9c296a163014328299a9345bd409b8f203772fd8dc954502841871f874R1-R43) [[3]](diffhunk://#diff-1513562a5281a737d81d7b060a1f2663f57aed0934c633f947cddf3af61d56bbR1-R170) * Updated `edit-ema-editor.component.html` to include the new block editor sidebar component and handle the `onSaved` event to reload the store. ### Improvements to Workflow Actions Service: * Modified `dot-workflow-actions-fire.service.ts` to use URLSearchParams for appending query parameters and refactored the handling of `inode` and `indexPolicy`. * Updated test cases in `dot-workflow-actions-fire.service.spec.ts` to reflect changes in the workflow actions fire service and ensure proper handling of the `indexPolicy` parameter. [[1]](diffhunk://#diff-edfb8c91327f7780ddeeaa0d3fd01655756ebdc77197088c6973460e0c38ddfaL185-R185) [[2]](diffhunk://#diff-edfb8c91327f7780ddeeaa0d3fd01655756ebdc77197088c6973460e0c38ddfaL198-R197) [[3]](diffhunk://#diff-edfb8c91327f7780ddeeaa0d3fd01655756ebdc77197088c6973460e0c38ddfaL218-R217) [[4]](diffhunk://#diff-edfb8c91327f7780ddeeaa0d3fd01655756ebdc77197088c6973460e0c38ddfaL236-R234) ### Event Handling and Test Cases: * Updated `dot-events.service.ts` to cast the filtered observable as `Observable<DotEvent<T>>`. * Added new test cases for the `DotBlockEditorSidebarComponent` to ensure correct behavior of the sidebar, block editor inputs, save functionality, and error handling. * Updated `edit-ema-editor.component.spec.ts` to include the new block editor sidebar component and handle the inline edit block editor event. [[1]](diffhunk://#diff-34ddc5fbacaf04b962f2037385ed284310d5faf35ba409d5705b2caadd5d796aR31) [[2]](diffhunk://#diff-34ddc5fbacaf04b962f2037385ed284310d5faf35ba409d5705b2caadd5d796aR82-R85) [[3]](diffhunk://#diff-34ddc5fbacaf04b962f2037385ed284310d5faf35ba409d5705b2caadd5d796aL111-R119) ### Minor Changes: * Added a method to inject the inline block editor in `dot-edit-content-html.service.ts`. ### Video https://github.com/user-attachments/assets/e6d59b8a-119a-4f93-87ae-44148541b86d ### Handle Error https://github.com/user-attachments/assets/8c606cce-f562-4fca-a9eb-50c6082eb383 --------- Co-authored-by: Jalinson Diaz <[email protected]>
- Loading branch information
Showing
22 changed files
with
803 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
...rtlet/src/lib/components/dot-block-editor-sidebar/dot-block-editor-sidebar.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<p-sidebar | ||
[blockScroll]="true" | ||
[closeOnEscape]="false" | ||
[dismissible]="false" | ||
[showCloseIcon]="false" | ||
[visible]="!!contentlet()" | ||
data-testId="sidebar" | ||
position="right"> | ||
<div class="container" (keydown.escape)="$event.stopPropagation()" data-testId="dot-container"> | ||
@if (contentlet(); as contentlet) { | ||
<dot-block-editor | ||
[field]="contentlet.field" | ||
[value]="contentlet.content" | ||
[languageId]="contentlet.languageId" | ||
(valueChange)="value.set($event)" | ||
data-testId="dot-block-editor" /> | ||
} | ||
<footer> | ||
<button | ||
pButton | ||
(click)="close()" | ||
[label]="'Cancel' | dm" | ||
[disabled]="loading()" | ||
class="p-button-outlined" | ||
data-testId="cancel-btn"></button> | ||
<button | ||
pButton | ||
(click)="saveEditorChanges()" | ||
[disabled]="!value() || loading()" | ||
[label]="'Save' | dm" | ||
[loading]="loading()" | ||
data-testId="save-btn"></button> | ||
</footer> | ||
</div> | ||
</p-sidebar> |
43 changes: 43 additions & 0 deletions
43
...rtlet/src/lib/components/dot-block-editor-sidebar/dot-block-editor-sidebar.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
@use "variables" as *; | ||
|
||
:host { | ||
.container { | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: space-between; | ||
} | ||
|
||
dot-block-editor { | ||
display: flex; | ||
flex-direction: column; | ||
height: calc(100% - 4rem); | ||
} | ||
} | ||
|
||
::ng-deep { | ||
p-sidebar { | ||
.p-sidebar-right { | ||
width: 50%; | ||
max-width: 1040px; | ||
} | ||
|
||
.p-sidebar-content { | ||
.container { | ||
height: 100%; | ||
} | ||
} | ||
} | ||
dot-block-editor .editor-wrapper { | ||
height: 100% !important; | ||
} | ||
} | ||
|
||
footer { | ||
display: flex; | ||
justify-content: flex-end; | ||
gap: $spacing-3; | ||
|
||
.p-button-secondary { | ||
margin-right: $spacing-3; | ||
} | ||
} |
206 changes: 206 additions & 0 deletions
206
...et/src/lib/components/dot-block-editor-sidebar/dot-block-editor-sidebar.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
import { byTestId, Spectator } from '@ngneat/spectator'; | ||
import { createComponentFactory } from '@ngneat/spectator/jest'; | ||
import { MockComponent } from 'ng-mocks'; | ||
import { of, throwError } from 'rxjs'; | ||
|
||
import { Sidebar } from 'primeng/sidebar'; | ||
|
||
import { BlockEditorModule, DotBlockEditorComponent } from '@dotcms/block-editor'; | ||
import { | ||
DotAlertConfirmService, | ||
DotContentTypeService, | ||
DotMessageService, | ||
DotWorkflowActionsFireService | ||
} from '@dotcms/data-access'; | ||
import { DotCMSContentType } from '@dotcms/dotcms-models'; | ||
import { | ||
dotcmsContentTypeBasicMock, | ||
MockDotMessageService, | ||
mockResponseView | ||
} from '@dotcms/utils-testing'; | ||
|
||
import { DotBlockEditorSidebarComponent } from './dot-block-editor-sidebar.component'; | ||
|
||
const BLOCK_EDITOR_FIELD = { | ||
clazz: 'com.dotcms.contenttype.model.field.ImmutableStoryBlockField', | ||
contentTypeId: '799f176a-d32e-4844-a07c-1b5fcd107578', | ||
dataType: 'LONG_TEXT', | ||
fieldType: 'Story-Block', | ||
fieldTypeLabel: 'Block Editor', | ||
fixed: false, | ||
iDate: 1649791703000, | ||
id: '71fe962eb681c5ffd6cd1623e5fc575a', | ||
indexed: false, | ||
listed: false, | ||
hint: 'A helper text', | ||
modDate: 1699364930000, | ||
name: 'Blog Content', | ||
readOnly: false, | ||
required: false, | ||
searchable: false, | ||
sortOrder: 13, | ||
unique: false, | ||
variable: 'testName', | ||
fieldVariables: [ | ||
{ | ||
clazz: 'com.dotcms.contenttype.model.field.ImmutableFieldVariable', | ||
fieldId: '71fe962eb681c5ffd6cd1623e5fc575a', | ||
id: 'b19e1d5d-47ad-40d7-b2bf-ccd0a5a86590', | ||
key: 'allowedBlocks', | ||
value: 'heading1' | ||
}, | ||
{ | ||
clazz: 'com.dotcms.contenttype.model.field.ImmutableFieldVariable', | ||
fieldId: '71fe962eb681c5ffd6cd1623e5fc575a', | ||
id: 'b19e1d5d-47ad-40d7-b2bf-ccd0a5a86590', | ||
key: 'allowedContentTypes', | ||
value: 'Activity' | ||
}, | ||
{ | ||
clazz: 'com.dotcms.contenttype.model.field.ImmutableFieldVariable', | ||
fieldId: '71fe962eb681c5ffd6cd1623e5fc575a', | ||
id: 'b19e1d5d-47ad-40d7-b2bf-ccd0a5a86590', | ||
key: 'styles', | ||
value: 'height:50%' | ||
} | ||
] | ||
}; | ||
|
||
const messageServiceMock = new MockDotMessageService({ | ||
'editpage.inline.error': 'An error occurred', | ||
error: 'Error' | ||
}); | ||
|
||
const EVENT_DATA = { | ||
fieldName: 'testName', | ||
contentType: 'Blog', | ||
language: '2', | ||
inode: 'testInode', | ||
blockEditorContent: '{"field":"field value"}' | ||
}; | ||
|
||
const contentTypeMock: DotCMSContentType = { | ||
...dotcmsContentTypeBasicMock, | ||
fields: [BLOCK_EDITOR_FIELD] | ||
}; | ||
|
||
describe('DotBlockEditorSidebarComponent', () => { | ||
let spectator: Spectator<DotBlockEditorSidebarComponent>; | ||
let dotContentTypeService: DotContentTypeService; | ||
let dotAlertConfirmService: DotAlertConfirmService; | ||
let dotWorkflowActionsFireService: DotWorkflowActionsFireService; | ||
|
||
const createComponent = createComponentFactory({ | ||
component: DotBlockEditorSidebarComponent, | ||
imports: [BlockEditorModule], | ||
declarations: [MockComponent(DotBlockEditorComponent)], | ||
providers: [ | ||
DotAlertConfirmService, | ||
{ provide: DotMessageService, useValue: messageServiceMock }, | ||
{ | ||
provide: DotWorkflowActionsFireService, | ||
useValue: { | ||
saveContentlet: jest.fn() | ||
} | ||
}, | ||
{ | ||
provide: DotContentTypeService, | ||
useValue: { | ||
getContentType: jest.fn().mockReturnValue(of(contentTypeMock)) | ||
} | ||
} | ||
] | ||
}); | ||
|
||
beforeEach(() => { | ||
spectator = createComponent(); | ||
dotContentTypeService = spectator.inject(DotContentTypeService, true); | ||
dotAlertConfirmService = spectator.inject(DotAlertConfirmService, true); | ||
dotWorkflowActionsFireService = spectator.inject(DotWorkflowActionsFireService, true); | ||
spectator.component.open(EVENT_DATA); | ||
spectator.detectChanges(); | ||
}); | ||
|
||
it('should set sidebar with correct inputs', () => { | ||
const sidebar = spectator.query(Sidebar); | ||
expect(sidebar.position).toBe('right'); | ||
expect(sidebar.blockScroll).toBe(true); | ||
expect(sidebar.dismissible).toBe(false); | ||
expect(sidebar.showCloseIcon).toBe(false); | ||
expect(sidebar.closeOnEscape).toBe(false); | ||
expect(sidebar.visible).toBe(true); | ||
}); | ||
|
||
it('should set inputs to the block editor', () => { | ||
const blockEditor = spectator.query(DotBlockEditorComponent); | ||
|
||
expect(blockEditor.field).toEqual(BLOCK_EDITOR_FIELD); | ||
expect(blockEditor.languageId).toBe(parseInt(EVENT_DATA.language)); | ||
expect(blockEditor.value).toEqual(JSON.parse(EVENT_DATA.blockEditorContent)); | ||
expect(dotContentTypeService.getContentType).toHaveBeenCalledWith('Blog'); | ||
}); | ||
|
||
it('should save changes in the editor', () => { | ||
const spyWorkflowService = jest.spyOn(dotWorkflowActionsFireService, 'saveContentlet'); | ||
const blockEditor = spectator.query(DotBlockEditorComponent); | ||
|
||
const newValue = { data: 'test value 1' }; | ||
blockEditor.valueChange.emit(newValue); | ||
|
||
spectator.detectChanges(); | ||
|
||
const saveBtn = spectator.query(byTestId('save-btn')) as HTMLButtonElement; | ||
|
||
saveBtn.click(); | ||
spectator.detectChanges(); | ||
|
||
expect(dotContentTypeService.getContentType).toHaveBeenCalledWith('Blog'); | ||
expect(spyWorkflowService).toHaveBeenCalledWith({ testName: JSON.stringify(newValue) }); | ||
}); | ||
|
||
it('should close the sidebar', () => { | ||
const cancelBtn = spectator.query(byTestId('cancel-btn')) as HTMLButtonElement; | ||
|
||
cancelBtn.click(); | ||
spectator.detectChanges(); | ||
|
||
const sidebar = spectator.query(Sidebar); | ||
|
||
expect(sidebar.visible).toBe(false); | ||
}); | ||
|
||
it('should display a toast on saving error', () => { | ||
const error404 = mockResponseView(404, '', null, { | ||
error: { message: 'An error occurred' } | ||
}); | ||
const dotAletConfirmServiceSpy = jest.spyOn(dotAlertConfirmService, 'alert'); | ||
const spyWorkflowService = jest | ||
.spyOn(dotWorkflowActionsFireService, 'saveContentlet') | ||
.mockReturnValue(throwError(error404)); | ||
|
||
const blockEditor = spectator.query(DotBlockEditorComponent); | ||
const newValue = { data: 'test value 1' }; | ||
blockEditor.valueChange.emit(newValue); | ||
|
||
spectator.detectChanges(); | ||
|
||
const saveBtn = spectator.query(byTestId('save-btn')) as HTMLButtonElement; | ||
saveBtn.click(); | ||
spectator.detectChanges(); | ||
|
||
expect(spyWorkflowService).toHaveBeenCalled(); | ||
expect(dotAletConfirmServiceSpy).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should call event.stopPropagation on escape keydown', () => { | ||
const event = new KeyboardEvent('keydown', { key: 'Escape' }); | ||
jest.spyOn(event, 'stopPropagation'); | ||
|
||
const container = spectator.query('[data-testId="dot-container"]'); | ||
container.dispatchEvent(event); | ||
|
||
expect(event.stopPropagation).toHaveBeenCalled(); | ||
}); | ||
|
||
afterEach(() => jest.clearAllMocks()); | ||
}); |
Oops, something went wrong.