Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/axonivy-market/marketplace
Browse files Browse the repository at this point in the history
… into feature/MARP-990-Market-website-Download-dropdown-at-wrong-place

# Conflicts:
#	marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.ts
  • Loading branch information
linhpd-axonivy committed Aug 29, 2024
2 parents 0c0d376 + a4a7d6c commit 50b9c10
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { LanguageService } from '../../../../core/services/language/language.ser
import { ItemDropdown } from '../../../../shared/models/item-dropdown.model';
import { ProductDetail } from '../../../../shared/models/product-detail.model';
import { environment } from '../../../../../environments/environment';
import { VERSION } from '../../../../shared/constants/common.constant';
@Component({
selector: 'app-product-version-action',
standalone: true,
Expand Down Expand Up @@ -166,7 +167,7 @@ export class ProductDetailVersionActionComponent implements AfterViewInit {
)
.subscribe(data => {
data.forEach(item => {
const version = 'Version '.concat(item.version);
const version = VERSION.displayPrefix.concat(item.version);
this.versions.update(currentVersions => [
...currentVersions,
version
Expand All @@ -187,7 +188,7 @@ export class ProductDetailVersionActionComponent implements AfterViewInit {
if (this.versions().length === 0) {
this.productService.sendRequestToGetProductVersionsForDesigner(this.productId
).subscribe(data => {
const versionMap = data.map((versionNumber: string) => 'Version '.concat(versionNumber));
const versionMap = data.map((versionNumber: string) => VERSION.displayPrefix.concat(versionNumber));
this.versions.set(versionMap);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ <h2 class="product-analysis mb-0">
<i [class]="productDetail().type | productTypeIcon"></i>
</h2>
<h4 class="analysis-title text-primary text-capitalize mb-0">
{{ productDetail().type }}
{{ productDetail().type | productType | translate }}
</h4>
</div>
</div>
Expand Down Expand Up @@ -116,7 +116,7 @@ <h4 class="analysis-title text-primary text-capitalize mb-0">
<div class="dropdown-row flex-row align-items-center w-100">
<div
class="dropdown-container position-relative d-flex align-items-center w-100">
<div id="tab-group-dropdown" class="flex-grow-1 w-100" >
<div id="tab-group-dropdown" class="flex-grow-1 w-100">
<app-common-dropdown
[items]="detailTabsForDropdown"
[selectedItem]="selectedTabLabel | translate"
Expand Down Expand Up @@ -151,58 +151,61 @@ <h4 class="analysis-title text-primary text-capitalize mb-0">
</div>
</div>
</div>

<div class="tab-content col-12 default-cursor">
<div
class="tab-pane fade show"
[class.active]="activeTab === 'description'"
id="description"
role="tabpanel"
aria-labelledby="description-tab">
<markdown
[lang]="languageService.selectedLanguage()"
class="readme-content"
[data]="
productModuleContent().description!
| multilingualism: languageService.selectedLanguage()
"></markdown>
</div>
<div
class="tab-pane fade"
[class.active]="activeTab === 'demo'"
id="demo"
role="tabpanel"
aria-labelledby="demo-tab">
<markdown
class="readme-content"
[data]="productModuleContent().demo!
| multilingualism: languageService.selectedLanguage()
"></markdown>
</div>
<div
class="tab-pane fade"
[class.active]="activeTab === 'setup'"
id="setup"
role="tabpanel"
aria-labelledby="setup-tab">
<markdown
class="readme-content"
[data]="productModuleContent().setup!
| multilingualism: languageService.selectedLanguage()
"></markdown>
</div>
<div
class="tab-pane fade"
[class.active]="activeTab === 'dependency'"
id="dependency"
role="tabpanel"
aria-labelledby="dependency-tab">
<app-product-detail-maven-content
[productModuleContent]="productModuleContent()"
[selectedVersion]="
selectedVersion
"></app-product-detail-maven-content>
</div>
@if (!isEmptyProductContent()) {
<div
class="tab-pane fade show"
[class.active]="activeTab === 'description'"
id="description"
role="tabpanel"
aria-labelledby="description-tab">
<markdown
[lang]="languageService.selectedLanguage()"
class="readme-content"
[data]="
productModuleContent().description!
| multilingualism: languageService.selectedLanguage()
"></markdown>
</div>
<div
class="tab-pane fade"
[class.active]="activeTab === 'demo'"
id="demo"
role="tabpanel"
aria-labelledby="demo-tab">
<markdown
class="readme-content"
[data]="
productModuleContent().demo!
| multilingualism: languageService.selectedLanguage()
"></markdown>
</div>
<div
class="tab-pane fade"
[class.active]="activeTab === 'setup'"
id="setup"
role="tabpanel"
aria-labelledby="setup-tab">
<markdown
class="readme-content"
[data]="
productModuleContent().setup!
| multilingualism: languageService.selectedLanguage()
"></markdown>
</div>
<div
class="tab-pane fade"
[class.active]="activeTab === 'dependency'"
id="dependency"
role="tabpanel"
aria-labelledby="dependency-tab">
<app-product-detail-maven-content
[productModuleContent]="productModuleContent()"
[selectedVersion]="
selectedVersion
"></app-product-detail-maven-content>
</div>
}
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ describe('ProductDetailComponent', () => {
});

it('should call setActiveTab and updateDropdownSelection on onTabChange', () => {
const event ={ value: 'description' };
const event = { value: 'description' };
spyOn(component, 'setActiveTab');
spyOn(component, 'updateDropdownSelection');

Expand All @@ -159,6 +159,15 @@ describe('ProductDetailComponent', () => {
expect(component.getContent('description')).toBeTrue();
});

it('should return false in tab visibility when product module content is missing', () => {
const mockEmptyContent: ProductModuleContent = {} as ProductModuleContent;
component.productModuleContent.set(mockEmptyContent);
expect(component.getContent('description')).toBeFalse();
expect(component.getContent('demo')).toBeFalse();
expect(component.getContent('setup')).toBeFalse();
expect(component.getContent('dependency')).toBeFalse();
});

it('should return false for description when it is null or empty', () => {
const mockContentWithEmptyDescription: ProductModuleContent =
MOCK_PRODUCT_MODULE_CONTENT;
Expand Down Expand Up @@ -195,6 +204,16 @@ describe('ProductDetailComponent', () => {
expect(component.getContent('setup')).toBeFalse();
});

it('should not display information when product detail is empty', () => {
const mockContentWithEmptySetup: ProductModuleContent =
{} as ProductModuleContent;
component.productModuleContent.set(mockContentWithEmptySetup);
expect(component.isEmptyProductContent()).toBeTrue();
fixture.detectChanges();
const description = fixture.debugElement.query(By.css('#description'));
expect(description).toBeFalsy();
});

it('should display dropdown horizontally on small viewport', () => {
viewport.set(540);
const tabGroup = fixture.debugElement.query(By.css('.row-tab'));
Expand Down Expand Up @@ -297,4 +316,22 @@ describe('ProductDetailComponent', () => {
component.onResize();
expect(component.checkMediaSize).toHaveBeenCalled();
});

it('should be empty selected version if product detail content is missing', () => {
component.productModuleContent.set({} as ProductModuleContent);
component.selectedVersion = '';
component.handleProductContentVersion();
expect(component.selectedVersion).toEqual('');
});

it('should be formated selected version if open in designer', () => {
const mockContent: ProductModuleContent = {
...MOCK_PRODUCT_MODULE_CONTENT,
tag: '10.0.11'
};
component.productModuleContent.set(mockContent);
routingQueryParamService.isDesignerEnv.and.returnValue(true);
component.handleProductContentVersion();
expect(component.selectedVersion).toEqual('Version 10.0.11');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import { CommonModule } from '@angular/common';
import { ProductDetailInformationTabComponent } from './product-detail-information-tab/product-detail-information-tab.component';
import { ProductDetailVersionActionComponent } from './product-detail-version-action/product-detail-version-action.component';
import { ProductDetailMavenContentComponent } from './product-detail-maven-content/product-detail-maven-content.component';
import { PRODUCT_DETAIL_TABS } from '../../../shared/constants/common.constant';
import {
PRODUCT_DETAIL_TABS,
VERSION
} from '../../../shared/constants/common.constant';
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { LanguageService } from '../../../core/services/language/language.service';
import { MultilingualismPipe } from '../../../shared/pipes/multilingualism.pipe';
Expand All @@ -35,6 +38,7 @@ import { RoutingQueryParamService } from '../../../shared/services/routing.query
import { CommonDropdownComponent } from '../../../shared/components/common-dropdown/common-dropdown.component';
import { CommonUtils } from '../../../shared/utils/common.utils';
import { ItemDropdown } from '../../../shared/models/item-dropdown.model';
import { ProductTypePipe } from '../../../shared/pipes/product-type.pipe';

export interface DetailTab {
activeClass: string;
Expand All @@ -58,6 +62,7 @@ const DEFAULT_ACTIVE_TAB = 'description';
ProductDetailMavenContentComponent,
NgbNavModule,
MultilingualismPipe,
ProductTypePipe,
ProductDetailFeedbackComponent,
ProductInstallationCountActionComponent,
ProductTypeIconPipe,
Expand Down Expand Up @@ -107,7 +112,6 @@ export class ProductDetailComponent {
this.updateDropdownSelection();
}


constructor() {
this.scrollToTop();
this.resizeObserver = new ResizeObserver(() => {
Expand All @@ -122,17 +126,11 @@ export class ProductDetailComponent {
this.getProductById(productId).subscribe(productDetail => {
this.productDetail.set(productDetail);
this.productModuleContent.set(productDetail.productModuleContent);
if (this.routingQueryParamService.isDesignerEnv()) {
this.selectedVersion = 'Version '.concat(this.convertTagToVersion((productDetail.productModuleContent.tag)));
}
this.detailTabsForDropdown = this.getNotEmptyTabs();
this.productDetailService.productNames.set(productDetail.names);
localStorage.removeItem(STORAGE_ITEM);
this.installationCount = productDetail.installationCount;
this.selectedVersion = this.productModuleContent().tag;
if (this.selectedVersion.startsWith('v')) {
this.selectedVersion = this.selectedVersion.substring(1);
}
this.handleProductContentVersion();
});
this.productFeedbackService.initFeedbacks();
this.productStarRatingService.fetchData();
Expand All @@ -145,6 +143,18 @@ export class ProductDetailComponent {
this.updateDropdownSelection();
}

handleProductContentVersion() {
if (this.isEmptyProductContent()) {
return;
}
this.selectedVersion = this.convertTagToVersion(
this.productModuleContent().tag
);
if (this.routingQueryParamService.isDesignerEnv()) {
this.selectedVersion = VERSION.displayPrefix.concat(this.selectedVersion);
}
}

scrollToTop() {
window.scrollTo({ left: 0, top: 0, behavior: 'instant' });
}
Expand Down Expand Up @@ -177,20 +187,28 @@ export class ProductDetailComponent {
}

getContent(value: string): boolean {
if (this.isEmptyProductContent()) {
return false;
}
const content = this.productModuleContent();
const conditions: { [key: string]: boolean } = {
description: content.description !== null,
demo: content.demo !== null,
setup: content.setup !== null ,
setup: content.setup !== null,
dependency: content.isDependency
};

return conditions[value] ?? false;
}

isEmptyProductContent(): boolean {
const content = this.productModuleContent();
return !content || Object.keys(content).length === 0;
}

loadDetailTabs(selectedVersion: string) {
let version = selectedVersion || this.productDetail().newestReleaseVersion;
version = version.replace("Version ","")
version = version.replace(VERSION.displayPrefix, '');
this.productService
.getProductDetailsWithVersion(this.productDetail().id, version)
.subscribe(updatedProductDetail => {
Expand Down Expand Up @@ -240,10 +258,11 @@ export class ProductDetailComponent {

@HostListener('document:click', ['$event'])
handleClickOutside(event: MouseEvent) {
const formSelect =
this.elementRef.nativeElement.querySelector('.form-select');
if (
!this.elementRef.nativeElement
.querySelector('.form-select')
.contains(event.target) &&
formSelect &&
!formSelect.contains(event.target) &&
this.isTabDropdownShown()
) {
this.onTabDropdownShown();
Expand Down Expand Up @@ -288,8 +307,8 @@ export class ProductDetailComponent {
return this.detailTabsForDropdown.filter(tab => this.getContent(tab.value));
}

convertTagToVersion(tag: string) : string {
if (tag !== '' && tag.startsWith('v')){
convertTagToVersion(tag: string): string {
if (tag !== '' && tag.startsWith(VERSION.tagPrefix)) {
return tag.substring(1);
}
return tag;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
'border-light text-light': themeService.isDarkMode(),
'border-dark text-dark': !themeService.isDarkMode(),
'bg-light text-dark':
selectedTypeLabel === type.value && themeService.isDarkMode(),
selectedTypeLabel === type.label && themeService.isDarkMode(),
'bg-primary border-0 text-light':
selectedTypeLabel === type.value && !themeService.isDarkMode()
selectedTypeLabel === type.label && !themeService.isDarkMode()
}">
<p [lang]="languageService.selectedLanguage()">
{{ type.label | translate }}
Expand Down
5 changes: 5 additions & 0 deletions marketplace-ui/src/app/shared/constants/common.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,8 @@ export const DEFAULT_PAGEABLE_IN_REST_CLIENT: Pageable = {
page: 0,
size: 40
};

export const VERSION = {
tagPrefix: 'v',
displayPrefix: 'Version '
};
11 changes: 11 additions & 0 deletions marketplace-ui/src/app/shared/pipes/product-type.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
standalone: true,
name: 'productType'
})
export class ProductTypePipe implements PipeTransform {
transform(type: string, _args?: []): string {
return `common.filter.value.${type}`;
}
}
Loading

0 comments on commit 50b9c10

Please sign in to comment.