Skip to content

Commit

Permalink
feat(edit-content) fix small issues (#31289)
Browse files Browse the repository at this point in the history
### Proposed Changes
* Sticky tabs
* Fix iframe height detection.
* Remember tabs, sidebar, and tabs inside sidebar selected with local
store

### Checklist
- [ ] Tests
- [ ] Translations
- [ ] Security Implications Contemplated (add notes if applicable)

### Additional Info
** any additional useful context or info **

### Screenshots
Original             |  Updated
:-------------------------:|:-------------------------:
** original screenshot **  |  ** updated screenshot **
  • Loading branch information
oidacra authored Feb 10, 2025
1 parent d35d18e commit c891857
Show file tree
Hide file tree
Showing 20 changed files with 501 additions and 206 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,5 @@ dotCMS/dependencies.gradle
.nx/
.cursorrules
.cursorignore
**/vite.config.mts.timestamp-*
**/vite.config.mts.timestamp-*
.cursor/
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
@let contentType = $store.contentType();
@let contentlet = $store.contentlet();
@let showSidebar = $store.showSidebar();
@let showSidebar = $store.isSidebarOpen();
@let actions = $store.getActions();
@let showWorkflowActions = $store.showWorkflowActions();
@let activeIndex = $store.activeTab();

<!--Locales-->
@let currentLocale = $store.currentLocale();
Expand All @@ -16,6 +17,8 @@
[scrollable]="true"
data-testId="edit-content-multiple-tabs"
[class.dot-edit-content-tabview--single-tab]="$hasSingleTab()"
(onChange)="onActiveIndexChange($event)"
[activeIndex]="activeIndex"
class="dot-edit-content-tabview">
<!-- Add prepend and append areas to the tabs -->
<ng-template
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ $tab-min-height: 52px;
border: solid 1px $color-palette-gray-300;
border-right: none;
overflow: initial;
position: initial;
position: sticky;
top: 0;
background-color: $white;
z-index: 1000;
}

.p-tabview-nav-content .p-tabview-nav {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
import { Router } from '@angular/router';

import { ButtonModule } from 'primeng/button';
import { TabViewModule } from 'primeng/tabview';
import { TabViewChangeEvent, TabViewModule } from 'primeng/tabview';

import { DotCMSContentlet, DotCMSContentTypeField } from '@dotcms/dotcms-models';
import { DotMessagePipe, DotWorkflowActionsComponent } from '@dotcms/ui';
Expand Down Expand Up @@ -418,4 +418,14 @@ export class DotEditContentFormComponent implements OnInit {

window.open(realUrl, '_blank');
}

/**
* Sets the active tab index in the store when the tab view changes.
*
* @param {TabViewChangeEvent} event - The event object containing the active tab index.
* @memberof DotEditContentFormComponent
*/
onActiveIndexChange({ index }: TabViewChangeEvent) {
this.$store.setActiveTab(index);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@if ($store.isLoaded() || $store.isSaving()) {
@let contentType = $store.contentType();
@let variable = contentType.variable;
@let showSidebar = $store.showSidebar();
@let showSidebar = $store.isSidebarOpen();
@let showSelectWorkflowWarning = $store.showSelectWorkflowWarning();

<div class="edit-content-layout__topBar" data-testId="edit-content-layout__topBar">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('EditContentLayoutComponent', () => {
MockComponent(DotEditContentSidebarComponent)
],
componentProviders: [
DotEditContentStore, // Usign the real DotEditContentStore
DotEditContentStore,
mockProvider(DotWorkflowsActionsService),
mockProvider(DotWorkflowActionsFireService),
mockProvider(DotEditContentService),
Expand All @@ -78,8 +78,6 @@ describe('EditContentLayoutComponent', () => {
{
provide: ActivatedRoute,
useValue: {
// Provide an empty snapshot to bypass the Store's onInit,
// allowing direct method calls for testing
get snapshot() {
return { params: { id: undefined, contentType: undefined } };
}
Expand All @@ -90,7 +88,6 @@ describe('EditContentLayoutComponent', () => {
url: '/test-url',
events: of()
}),

provideHttpClient(),
provideHttpClientTesting()
]
Expand All @@ -111,8 +108,12 @@ describe('EditContentLayoutComponent', () => {

jest.spyOn(dotLanguagesService, 'get').mockReturnValue(of(MOCK_LANGUAGES));

// By default, the local storage is set to true
jest.spyOn(utils, 'getPersistSidebarState').mockReturnValue(true);
// Mock the initial UI state
jest.spyOn(utils, 'getStoredUIState').mockReturnValue({
activeTab: 0,
isSidebarOpen: true,
activeSidebarTab: 0
});
});

it('should have p-confirmDialog component', () => {
Expand All @@ -136,7 +137,7 @@ describe('EditContentLayoutComponent', () => {
tick(); // Wait for the defer to load

expect(store.isEnabledNewContentEditor()).toBe(true);
expect(store.showSidebar()).toBe(true);
expect(store.isSidebarOpen()).toBe(true);

expect(spectator.query(byTestId('edit-content-layout__topBar'))).toBeTruthy();
expect(spectator.query(byTestId('edit-content-layout__body'))).toBeTruthy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import { DotEditContentSidebarComponent } from '../dot-edit-content-sidebar/dot-
],

host: {
'[class.edit-content--with-sidebar]': '$store.showSidebar()'
'[class.edit-content--with-sidebar]': '$store.isSidebarOpen()'
},
templateUrl: './dot-edit-content.layout.component.html',
styleUrls: ['./dot-edit-content.layout.component.scss'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
@let contentlet = store.contentlet();
@let contentType = store.contentType();
@let contentlet = $store.contentlet();
@let contentType = $store.contentType();
@let activeIndex = $store.activeSidebarTab();

<!-- Information Component -->
@let isLoadingInformation = store.isLoadingInformation();
@let referencePagesCount = store.information.relatedContent();
@let isLoadingInformation = $store.isLoadingInformation();
@let referencePagesCount = $store.information.relatedContent();

<aside class="content-sidebar">
<p-tabView>
<p-tabView
[activeIndex]="activeIndex"
(onChange)="onActiveIndexChange($event)"
data-testId="sidebar-tabs">
<!-- Add prepend and append areas to the tabs -->
<ng-template dotTabViewInsert [dotTabViewAppend]="appendContent"></ng-template>

<p-tabPanel [header]="'Info' | dm">
<!-- Information section -->
<dot-edit-content-sidebar-section [title]="'edit.content.sidebar.general.title' | dm">
<ng-template #sectionAction>
@if (!store.isNew() && !store.isCopyingLocale()) {
@if (!$store.isNew() && !$store.isCopyingLocale()) {
<dot-copy-button
[copy]="contentlet.identifier"
[label]="contentlet.shortyId"
Expand All @@ -33,11 +38,11 @@

<dot-edit-content-sidebar-section [title]="'edit.content.sidebar.locales' | dm">
<dot-edit-content-sidebar-locales
[locales]="store.locales()"
[defaultLocale]="store.systemDefaultLocale()"
[currentLocale]="store.currentLocale()"
[isLoading]="store.isLoadingLocales()"
(switchLocale)="store.switchLocale($event)"
[locales]="$store.locales()"
[defaultLocale]="$store.systemDefaultLocale()"
[currentLocale]="$store.currentLocale()"
[isLoading]="$store.isLoadingLocales()"
(switchLocale)="$store.switchLocale($event)"
data-testId="locales" />
</dot-edit-content-sidebar-section>

Expand All @@ -47,11 +52,11 @@
<dot-edit-content-sidebar-workflow
[workflowSelection]="$workflowSelection()"
[(showDialog)]="$showDialog"
(onSelectWorkflow)="store.setSelectedWorkflow($event)"
[resetWorkflowAction]="store.getResetWorkflowAction()"
(onSelectWorkflow)="$store.setSelectedWorkflow($event)"
[resetWorkflowAction]="$store.getResetWorkflowAction()"
(onResetWorkflow)="fireWorkflowAction($event)"
[workflow]="$workflow()"
[isLoading]="store.isLoadingWorkflow()"
[isLoading]="$store.isLoadingWorkflow()"
data-testId="workflow" />
</dot-edit-content-sidebar-section>
</p-tabPanel>
Expand All @@ -74,7 +79,7 @@
<div class="content-sidebar__close">
<button
class="p-button p-button-sm p-button-text p-button-rounded p-button-icon-only"
(click)="store.toggleSidebar()"
(click)="$store.toggleSidebar()"
data-testId="toggle-button">
<svg
width="20"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ import { DotEditContentSidebarComponent } from './dot-edit-content-sidebar.compo
import { DotEditContentService } from '../../services/dot-edit-content.service';
import { DotEditContentStore } from '../../store/edit-content.store';
import { MOCK_WORKFLOW_STATUS } from '../../utils/edit-content.mock';
import * as utils from '../../utils/functions.util';
import { MockResizeObserver } from '../../utils/mocks';

describe('DotEditContentSidebarComponent', () => {
let spectator: Spectator<DotEditContentSidebarComponent>;
let dotEditContentService: SpyObject<DotEditContentService>;
let dotWorkflowService: SpyObject<DotWorkflowService>;
let store: SpyObject<InstanceType<typeof DotEditContentStore>>;

const createComponent = createComponentFactory({
component: DotEditContentSidebarComponent,
Expand All @@ -46,7 +48,7 @@ describe('DotEditContentSidebarComponent', () => {
MockComponent(DotEditContentSidebarWorkflowComponent)
],
providers: [
DotEditContentStore, // Due using the store directly
DotEditContentStore,
mockProvider(DotWorkflowsActionsService),
mockProvider(DotWorkflowActionsFireService),
mockProvider(DotEditContentService),
Expand All @@ -62,8 +64,6 @@ describe('DotEditContentSidebarComponent', () => {
{
provide: ActivatedRoute,
useValue: {
// Provide an empty snapshot to bypass the Store's onInit,
// allowing direct method calls for testing
get snapshot() {
return { params: { id: undefined, contentType: undefined } };
}
Expand All @@ -76,8 +76,17 @@ describe('DotEditContentSidebarComponent', () => {
window.ResizeObserver = MockResizeObserver;
spectator = createComponent({ detectChanges: false });

store = spectator.inject(DotEditContentStore, true);
dotEditContentService = spectator.inject(DotEditContentService);
dotWorkflowService = spectator.inject(DotWorkflowService);

// Mock the initial UI state
jest.spyOn(utils, 'getStoredUIState').mockReturnValue({
activeTab: 0,
isSidebarOpen: true,
activeSidebarTab: 0
});

dotEditContentService.getReferencePages.mockReturnValue(of(1));
dotWorkflowService.getWorkflowStatus.mockReturnValue(of(MOCK_WORKFLOW_STATUS));

Expand Down Expand Up @@ -117,10 +126,28 @@ describe('DotEditContentSidebarComponent', () => {
});

it('should call toggleSidebar when toggle button is clicked', () => {
const storeSpy = jest.spyOn(spectator.component.store, 'toggleSidebar');
const storeSpy = jest.spyOn(store, 'toggleSidebar');

spectator.click(byTestId('toggle-button'));

expect(storeSpy).toHaveBeenCalled();
});

describe('UI State', () => {
it('should initialize with correct UI state', () => {
expect(store.isSidebarOpen()).toBe(true);
expect(store.activeSidebarTab()).toBe(0);
});

it('should update active tab when changed', () => {
store.setActiveSidebarTab(1);
expect(store.activeSidebarTab()).toBe(1);
});

it('should toggle sidebar visibility', () => {
const initialState = store.isSidebarOpen();
store.toggleSidebar();
expect(store.isSidebarOpen()).toBe(!initialState);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { ButtonModule } from 'primeng/button';
import { DialogModule } from 'primeng/dialog';
import { DropdownModule } from 'primeng/dropdown';
import { TabViewModule } from 'primeng/tabview';
import { TabViewChangeEvent, TabViewModule } from 'primeng/tabview';

import { DotEditContentSidebarLocalesComponent } from '@dotcms/edit-content/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-locales/dot-edit-content-sidebar-locales.component';
import { DotCopyButtonComponent, DotMessagePipe } from '@dotcms/ui';
Expand Down Expand Up @@ -50,29 +50,29 @@ import { DotEditContentStore } from '../../store/edit-content.store';
]
})
export class DotEditContentSidebarComponent {
readonly store: InstanceType<typeof DotEditContentStore> = inject(DotEditContentStore);
readonly $identifier = this.store.getCurrentContentIdentifier;
readonly $formValues = this.store.formValues;
readonly $contentType = this.store.contentType;
readonly $contentlet = this.store.contentlet;
readonly $store: InstanceType<typeof DotEditContentStore> = inject(DotEditContentStore);
readonly $identifier = this.$store.getCurrentContentIdentifier;
readonly $formValues = this.$store.formValues;
readonly $contentType = this.$store.contentType;
readonly $contentlet = this.$store.contentlet;

/**
* Computed property that returns the workflow state of the content.
*/
readonly $workflow = computed<DotWorkflowState | null>(() => ({
scheme: this.store.getScheme(),
step: this.store.getCurrentStep(),
task: this.store.lastTask(),
contentState: this.store.initialContentletState(),
resetAction: this.store.getResetWorkflowAction()
readonly $workflow = computed<DotWorkflowState>(() => ({
scheme: this.$store.getScheme(),
step: this.$store.getCurrentStep(),
task: this.$store.lastTask(),
contentState: this.$store.initialContentletState(),
resetAction: this.$store.getResetWorkflowAction()
}));

/**
* Computed property that returns the workflow selection state.
*/
readonly $workflowSelection = computed(() => ({
schemeOptions: this.store.workflowSchemeOptions(),
isWorkflowSelected: this.store.showSelectWorkflowWarning()
schemeOptions: this.$store.workflowSchemeOptions(),
isWorkflowSelected: this.$store.showSelectWorkflowWarning()
}));

/**
Expand All @@ -90,13 +90,17 @@ export class DotEditContentSidebarComponent {

untracked(() => {
if (identifier) {
this.store.getReferencePages(identifier);
this.$store.getReferencePages(identifier);
}
});
});

/**
* Fires a workflow action.
* @param actionId - The ID of the action to fire.
*/
fireWorkflowAction(actionId: string): void {
this.store.fireWorkflowAction({
this.$store.fireWorkflowAction({
actionId,
inode: this.$contentlet().inode,
data: {
Expand All @@ -107,4 +111,13 @@ export class DotEditContentSidebarComponent {
}
});
}

/**
* Handles the active index change event from the sidebar tabs.
* @param $event - The event object containing the active index.
*/
onActiveIndexChange($event: TabViewChangeEvent) {
const { index } = $event;
this.$store.setActiveSidebarTab(index);
}
}
Loading

0 comments on commit c891857

Please sign in to comment.