Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added tiles widgets for product apis and products list #1162

Merged
merged 1 commit into from
Feb 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/apim.runtime.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { ProductSubscribe } from "./components/products/product-subscribe/ko/run
import { DefaultAuthenticator } from "./components/defaultAuthenticator";
import { Spinner } from "./components/spinner/spinner";
import { ProductApis } from "./components/products/product-apis/ko/runtime/product-apis";
import { ProductApisTiles } from "./components/products/product-apis/ko/runtime/product-apis-tiles";
import { OperationList } from "./components/operations/operation-list/ko/runtime/operation-list";
import { ProductSubscriptions } from "./components/products/product-subscriptions/ko/runtime/product-subscriptions";
import { AadService } from "./services/aadService";
Expand All @@ -71,6 +72,7 @@ import { OAuthService } from "./services/oauthService";
import { DefaultSessionManager } from "./authentication/defaultSessionManager";
import { ApiProducts } from "./components/apis/api-products/ko/runtime/api-products";
import { ApiProductsTiles } from "./components/apis/api-products/ko/runtime/api-products-tiles";
import { ProductListTiles } from "./components/products/product-list/ko/runtime/product-list-tiles";

export class ApimRuntimeModule implements IInjectorModule {
public register(injector: IInjector): void {
Expand Down Expand Up @@ -110,11 +112,13 @@ export class ApimRuntimeModule implements IInjectorModule {
injector.bind("subscriptions", Subscriptions);
injector.bind("productList", ProductList);
injector.bind("productListDropdown", ProductListDropdown);
injector.bind("productListTiles", ProductListTiles);
injector.bind("validationSummary", ValidationSummary);
injector.bind("productDetails", ProductDetails);
injector.bind("productSubscribe", ProductSubscribe);
injector.bind("productSubscriptions", ProductSubscriptions);
injector.bind("productApis", ProductApis);
injector.bind("productApisTiles", ProductApisTiles);
injector.bind("operationList", OperationList);
injector.bind("operationDetails", OperationDetails);
injector.bind("usersService", UsersService);
Expand Down
2 changes: 1 addition & 1 deletion src/components/apis/api-products/apiProductsHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class ApiProductsHandlers implements IWidgetHandler {
displayName: "API: products",
iconClass: "paperbits-cheque-3",
requires: ["html"],
createModel: async () => new ApiProductsModel()
createModel: async () => new ApiProductsModel("list")
};

return widgetOrder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class ApiProductsViewModelBinder implements ViewModelBinder<ApiProductsMo
}));

viewModel["widgetBinding"] = {
displayName: "API: products",
displayName: "API: products" + (model.layout === "list" ? "" : ` (${model.layout})`),
model: model,
draggable: true,
editor: "api-products-editor",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class ListOfApisViewModelBinder implements ViewModelBinder<ListOfApisMode
}));

viewModel["widgetBinding"] = {
displayName: "List of APIs",
displayName: "List of APIs" + (model.layout === "list" ? "" : ` (${model.layout})`),
model: model,
draggable: true,
editor: "list-of-apis-editor",
Expand Down
6 changes: 6 additions & 0 deletions src/components/products/product-apis/ko/productApis.html
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
<!-- ko if: layout() === 'list' -->
<product-apis-runtime data-bind="attr: { params: runtimeConfig }"></product-apis-runtime>
<!-- /ko -->

<!-- ko if: layout() === 'tiles' -->
<product-apis-tiles-runtime data-bind="attr: { params: runtimeConfig }"></product-apis-tiles-runtime>
<!-- /ko -->
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { IInjectorModule, IInjector } from "@paperbits/common/injection";
import { ProductApisHandlers } from "../productApisHandlers";
import { ProductApisHandlers, ProductApisTilesHandlers } from "../productApisHandlers";
import { ProductApisEditor } from "./productApisEditor";

export class ProductApisEditorModule implements IInjectorModule {
public register(injector: IInjector): void {
injector.bind("productApisEditor", ProductApisEditor);
injector.bindToCollection("widgetHandlers", ProductApisHandlers, "productApisHandlers");
injector.bindToCollection("widgetHandlers", ProductApisTilesHandlers, "productApisTilesHandlers");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import { Component } from "@paperbits/common/ko/decorators/component.decorator";
template: template
})
export class ProductApisViewModel {
public readonly layout: ko.Observable<string>;
public readonly runtimeConfig: ko.Observable<string>;

constructor() {
this.layout = ko.observable();
this.runtimeConfig = ko.observable();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ export class ProductApisViewModelBinder implements ViewModelBinder<ProductApisMo
viewModel = new ProductApisViewModel();
}

viewModel.layout(model.layout);

viewModel.runtimeConfig(JSON.stringify({
detailsPageUrl: model.detailsPageHyperlink
? model.detailsPageHyperlink.href
: undefined
}));

viewModel["widgetBinding"] = {
displayName: "Product: APIs",
displayName: "Product: APIs" + (model.layout === "list" ? "" : ` (${model.layout})`),
model: model,
draggable: true,
editor: "product-apis-editor",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<div class="form-inline search-input">
<div class="input-group">
<input type="search" role="searchbox" aria-label="Search" placeholder="Search APIs" spellcheck="false"
data-bind="textInput: pattern" />
<button type="button" class="search-button" aria-label="Search products">
<i class="icon-emb icon-emb-magnifier"></i>
</button>
</div>
</div>

<div class="cards">
<!-- ko if: working -->
<div class="cards-body">
<spinner class="fit"></spinner>
</div>
<!-- /ko -->

<!-- ko ifnot: working -->
<div class="cards-body animation-fade-in">

<!-- ko foreach: { data: apis, as: 'item' } -->
<a href="#" data-bind="attr: { href: $component.getReferenceUrl(item) }">
<div class="card item-tile">
<h3>
<span data-bind="text: item.displayName"></span>
<!-- ko if: item.type === 'soap' -->
<span class="badge badge-soap">SOAP</span>
<!-- /ko -->
<!-- ko if: item.apiVersion -->
- <span data-bind="text: item.apiVersion"></span>
<!-- /ko -->
</h3>
<div class="tile line-clamp">
<p class="tile-content" data-bind="markdown: { source: item.description, truncateAt: 250 }"></p>
</div>
</div>
</a>
<!-- /ko -->

<!-- ko if: apis().length === 0 -->
<p>This product doesn't have APIs.</p>
<!-- /ko -->

</div>
<!-- /ko -->

<div class="cards-footer">
<!-- ko if: hasPager -->
<ul class="pagination" role="navigation" aria-label="Pagination">
<!-- ko if: hasPrevPage -->
<li class="page-item">
<a href="#" class="page-link" role="button" aria-label="Previous page"
data-bind="click: prevPage, enable: hasPrevPage">
<i class="icon-emb icon-emb-chevron-left"></i>
</a>
</li>
<!-- /ko -->
<li class="page-item">
<span class="page-link" data-bind="text: page"></span>
</li>
<!-- ko if: hasNextPage -->
<li class="page-item">
<a href="#" class="page-link" role="button" aria-label="Next page"
data-bind="click: nextPage, enable: hasNextPage">
<i class="icon-emb icon-emb-chevron-right"></i>
</a>
</li>
<!-- /ko -->
</ul>
<!-- /ko -->
</div>
</div>
120 changes: 120 additions & 0 deletions src/components/products/product-apis/ko/runtime/product-apis-tiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import * as ko from "knockout";
import * as Constants from "../../../../../constants";
import template from "./product-apis-tiles.html";
import { Component, RuntimeComponent, OnMounted, OnDestroyed, Param } from "@paperbits/common/ko/decorators";
import { Router } from "@paperbits/common/routing";
import { ApiService } from "../../../../../services/apiService";
import { Api } from "../../../../../models/api";
import { SearchQuery } from "../../../../../contracts/searchQuery";
import { RouteHelper } from "../../../../../routing/routeHelper";


@RuntimeComponent({
selector: "product-apis-tiles-runtime"
})
@Component({
selector: "product-apis-tiles-runtime",
template: template
})
export class ProductApisTiles {
public readonly apis: ko.ObservableArray<Api>;
public readonly working: ko.Observable<boolean>;
public readonly pattern: ko.Observable<string>;
public readonly page: ko.Observable<number>;
public readonly hasPager: ko.Computed<boolean>;
public readonly hasPrevPage: ko.Observable<boolean>;
public readonly hasNextPage: ko.Observable<boolean>;

constructor(
private readonly apiService: ApiService,
private readonly router: Router,
private readonly routeHelper: RouteHelper
) {
this.detailsPageUrl = ko.observable();
this.apis = ko.observableArray([]);
this.working = ko.observable();
this.pattern = ko.observable();
this.page = ko.observable(1);
this.hasPrevPage = ko.observable();
this.hasNextPage = ko.observable();
this.hasPager = ko.computed(() => this.hasPrevPage() || this.hasNextPage());
}

@Param()
public detailsPageUrl: ko.Observable<string>;

@OnMounted()
public async initialize(): Promise<void> {
await this.searchApis();

this.router.addRouteChangeListener(this.searchApis);

this.pattern
.extend({ rateLimit: { timeout: Constants.defaultInputDelayMs, method: "notifyWhenChangesStop" } })
.subscribe(this.searchApis);
}

/**
* Initiates searching APIs.
*/
public async searchApis(): Promise<void> {
this.page(1);
this.loadPageOfApis();
}

/**
* Loads page of APIs.
*/
public async loadPageOfApis(): Promise<void> {
const productName = this.routeHelper.getProductName();

if (!productName) {
return;
}

try {
this.working(true);

const pageNumber = this.page() - 1;

const query: SearchQuery = {
pattern: this.pattern(),
skip: pageNumber * Constants.defaultPageSize,
take: Constants.defaultPageSize
};

const pageOfApis = await this.apiService.getProductApis(`products/${productName}`, query);
this.apis(pageOfApis.value);

const nextLink = pageOfApis.nextLink;

this.hasPrevPage(pageNumber > 0);
this.hasNextPage(!!nextLink);
}
catch (error) {
throw new Error(`Unable to load APIs. Error: ${error.message}`);
}
finally {
this.working(false);
}
}

public getReferenceUrl(api: Api): string {
return this.routeHelper.getApiReferenceUrl(api.name, this.detailsPageUrl());
}

public prevPage(): void {
this.page(this.page() - 1);
this.loadPageOfApis();
}

public nextPage(): void {
this.page(this.page() + 1);
this.loadPageOfApis();
}

@OnDestroyed()
public dispose(): void {
this.router.removeRouteChangeListener(this.searchApis);
}
}
5 changes: 5 additions & 0 deletions src/components/products/product-apis/productApisContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { HyperlinkContract } from "@paperbits/common/editing";
* Product API list widget configuration.
*/
export interface ProductApisContract extends Contract {
/**
* List layout. "list" or "tiles"
*/
itemStyleView?: string;

/**
* Link to a page that contains operation details.
*/
Expand Down
17 changes: 16 additions & 1 deletion src/components/products/product-apis/productApisHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,22 @@ export class ProductApisHandlers implements IWidgetHandler {
displayName: "Product: APIs",
iconClass: "paperbits-cheque-3",
requires: ["html"],
createModel: async () => new ProductApisModel()
createModel: async () => new ProductApisModel("list")
};

return widgetOrder;
}
}

export class ProductApisTilesHandlers implements IWidgetHandler {
public async getWidgetOrder(): Promise<IWidgetOrder> {
const widgetOrder: IWidgetOrder = {
name: "product-apis-tiles",
category: "Products",
displayName: "Product: APIs (tiles)",
iconClass: "paperbits-cheque-3",
requires: ["html"],
createModel: async () => new ProductApisModel("tiles")
};

return widgetOrder;
Expand Down
9 changes: 9 additions & 0 deletions src/components/products/product-apis/productApisModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@ import { HyperlinkModel } from "@paperbits/common/permalinks";
* Product API list widget configuration.
*/
export class ProductApisModel {
/**
* List layout. "list" or "tiles"
*/
public layout?: string;

/**
* Link to a page that contains API details.
*/
public detailsPageHyperlink: HyperlinkModel;

constructor(layout?: "list"|"tiles") {
this.layout = layout;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class ProductApisModelBinder implements IModelBinder<ProductApisModel> {

public async contractToModel(contract: ProductApisContract): Promise<ProductApisModel> {
const model = new ProductApisModel();
model.layout = contract.itemStyleView || "list";

if (contract.detailsPageHyperlink) {
model.detailsPageHyperlink = await this.permalinkResolver.getHyperlinkFromContract(contract.detailsPageHyperlink);
Expand All @@ -28,6 +29,7 @@ export class ProductApisModelBinder implements IModelBinder<ProductApisModel> {
public modelToContract(model: ProductApisModel): ProductApisContract {
const contract: ProductApisContract = {
type: "product-apis",
itemStyleView: model.layout,
detailsPageHyperlink: model.detailsPageHyperlink
? {
target: model.detailsPageHyperlink.target,
Expand Down
4 changes: 4 additions & 0 deletions src/components/products/product-list/ko/productList.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
<!-- ko if: layout() === 'dropdown' -->
<product-list-dropdown-runtime data-bind="attr: { params: runtimeConfig }"></product-list-dropdown-runtime>
<!-- /ko -->

<!-- ko if: layout() === 'tiles' -->
<product-list-tiles-runtime data-bind="attr: { params: runtimeConfig }"></product-list-tiles-runtime>
<!-- /ko -->
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { IInjectorModule, IInjector } from "@paperbits/common/injection";
import { ProductListHandlers, ProductListDropdownHandlers } from "../productListHandlers";
import { ProductListHandlers, ProductListDropdownHandlers, ProductListTilesHandlers } from "../productListHandlers";
import { ProductListEditor } from "./productListEditor";

export class ProductListEditorModule implements IInjectorModule {
public register(injector: IInjector): void {
injector.bind("productListEditor", ProductListEditor);
injector.bindToCollection("widgetHandlers", ProductListHandlers, "productListHandlers");
injector.bindToCollection("widgetHandlers", ProductListDropdownHandlers, "productListDropdownHandlers");
injector.bindToCollection("widgetHandlers", ProductListTilesHandlers, "productListTilesHandlers");
}
}
Loading