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

feat(content): resolve product, category and content links #775

Merged
merged 14 commits into from
Sep 19, 2023
92 changes: 90 additions & 2 deletions libs/domain/content/link/link.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { fixture } from '@open-wc/testing-helpers';
import { ContentService } from '@spryker-oryx/content';
import { createInjector, destroyInjector } from '@spryker-oryx/di';
import { ProductCategoryService, ProductService } from '@spryker-oryx/product';
import { RouteType } from '@spryker-oryx/router';
import { LinkService } from '@spryker-oryx/site';
import { IconComponent } from '@spryker-oryx/ui/icon';
Expand All @@ -15,6 +17,18 @@ class MockSemanticLinkService implements Partial<LinkService> {
get = vi.fn().mockReturnValue(of('/page'));
}

const mockCategoryService = {
get: vi.fn().mockReturnValue(of(null)),
};

const mockProductService = {
get: vi.fn().mockReturnValue(of(null)),
};

const mockContentService = {
get: vi.fn().mockReturnValue(of(null)),
};

describe('ContentLinkComponent', () => {
let element: ContentLinkComponent;
let semanticLinkService: MockSemanticLinkService;
Expand All @@ -30,6 +44,18 @@ describe('ContentLinkComponent', () => {
provide: LinkService,
useClass: MockSemanticLinkService,
},
{
provide: ProductCategoryService,
useValue: mockCategoryService,
},
{
provide: ProductService,
useValue: mockProductService,
},
{
provide: ContentService,
useValue: mockContentService,
},
],
});

Expand Down Expand Up @@ -95,25 +121,87 @@ describe('ContentLinkComponent', () => {
element = await fixture(
html`<oryx-content-link
.options=${{
type: RouteType.Product,
type: RouteType.Cart,
id: '123',
params: { foo: 'bar' },
}}
></oryx-content-link>`
);

mockContentService.get.mockReturnValue(of({ name: 'content' }));
});

it('should resolve the link from the LinkService', () => {
expect(semanticLinkService.get).toHaveBeenCalledWith({
type: RouteType.Product,
type: RouteType.Cart,
id: '123',
params: { foo: 'bar' },
});
});

it('should resolve the text from the ContentService', () => {
expect(mockContentService.get).toHaveBeenCalledWith({
id: '123',
type: RouteType.Cart,
});
});

it('should render the proper text', () => {
const link = element.renderRoot.querySelector('a');
expect(link?.textContent).toContain('content');
});

it('should build the correct link', () => {
expect(element).toContainElement('a[href="/page"]');
});

describe('when type is RouteType.Category', () => {
beforeEach(async () => {
element = await fixture(
html`<oryx-content-link
.options=${{
type: RouteType.Category,
id: '123',
}}
></oryx-content-link>`
);

mockCategoryService.get.mockReturnValue(of({ name: 'category' }));
});

it('should resolve the text from the ProductCategoryService', () => {
expect(mockCategoryService.get).toHaveBeenCalledWith('123');
});

it('should render the proper text', () => {
const link = element.renderRoot.querySelector('a');
expect(link?.textContent).toContain('category');
});
});

describe('when type is RouteType.Product', () => {
beforeEach(async () => {
element = await fixture(
html`<oryx-content-link
.options=${{
type: RouteType.Product,
id: '123',
}}
></oryx-content-link>`
);

mockProductService.get.mockReturnValue(of({ name: 'product' }));
});

it('should resolve the text from the ProductService', () => {
expect(mockProductService.get).toHaveBeenCalledWith({ sku: '123' });
});

it('should render the proper text', () => {
const link = element.renderRoot.querySelector('a');
expect(link?.textContent).toContain('product');
});
});
});

describe('when url and type are not provided', () => {
Expand Down
34 changes: 32 additions & 2 deletions libs/domain/content/link/link.component.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { ContentService } from '@spryker-oryx/content';
import { resolve } from '@spryker-oryx/di';
import { ContentMixin } from '@spryker-oryx/experience';
import { ProductCategoryService, ProductService } from '@spryker-oryx/product';
import { RouteType } from '@spryker-oryx/router';
import { LinkService } from '@spryker-oryx/site';
import { computed, hydrate } from '@spryker-oryx/utilities';

Check warning on line 7 in libs/domain/content/link/link.component.ts

View workflow job for this annotation

GitHub Actions / test-lint

'hydrate' is defined but never used
import { LitElement, TemplateResult, html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { when } from 'lit/directives/when.js';
import { map } from 'rxjs';
import { ContentLinkContent, ContentLinkOptions } from './link.model';

@hydrate()
export class ContentLinkComponent extends ContentMixin<

Check warning on line 15 in libs/domain/content/link/link.component.ts

View workflow job for this annotation

GitHub Actions / test-lint

'ContentLinkComponent' is defined but never used
ContentLinkOptions,
ContentLinkContent
>(LitElement) {
protected semanticLinkService = resolve(LinkService);
protected categoryService = resolve(ProductCategoryService);
protected productService = resolve(ProductService);
protected contentService = resolve(ContentService);

protected $link = computed(() => {
const { url, type, id, params } = this.$options();
Expand All @@ -36,8 +43,31 @@
</oryx-link>`;
}

protected $text = computed(() => {
const { type, id } = this.$options();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could move this down a bit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick, but still open afaik

const { text } = this.$content() ?? {};

if (text) return text;

if (type === RouteType.Category && id) {
return this.categoryService
.get(id)
.pipe(map((category) => category?.name));
}

if (type === RouteType.Product && id) {
return this.productService
.get({ sku: id })
.pipe(map((product) => product?.name));
}

return this.contentService
.get({ type, id })
.pipe(map((content) => content?.name));
});

protected renderLink(custom?: boolean): TemplateResult {
if (!this.$link()) return html`${this.$content()?.text}`;
if (!this.$link()) return html`${this.$text()}`;

const { label, target } = this.$options();

Expand All @@ -56,7 +86,7 @@
}

protected renderContent(): TemplateResult {
const { text } = this.$content() ?? {};
const text = this.$text();
const { button, icon } = this.$options();
const renderIcon = !!button && !!icon;

Expand Down
121 changes: 54 additions & 67 deletions libs/domain/content/link/link.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,71 +5,58 @@ import { ColorType } from '@spryker-oryx/ui/link';
import { iconInjectable } from '@spryker-oryx/utilities';
import { ContentLinkComponent } from './link.component';

export const linkComponentSchema: ContentComponentSchema<ContentLinkComponent> =
{
name: 'Link',
group: 'Content',
icon: IconTypes.Link,
options: {
url: { type: FormFieldType.Text },
type: {
type: FormFieldType.Select,
options: [
{
value: 'page',
text: 'Page',
},
{
value: 'product',
text: 'Product',
},
{
value: 'category',
text: 'Category',
},
],
},
target: {
type: FormFieldType.Select,
options: [
{
value: '_blank',
text: '_blank',
},
{
value: '_self',
text: '_self',
},
{
value: '_parent',
text: '_parent',
},
{
value: '_top',
text: '_top',
},
],
},
id: {
label: 'Product/Category/Page ID',
type: FormFieldType.Text,
},
color: {
type: FormFieldType.Select,
options: [{ value: ColorType.Primary }, { value: ColorType.Neutral }],
},
icon: {
type: FormFieldType.Select,
options:
iconInjectable
.get()
?.getIcons()
.sort()
.map((i) => ({ value: i, text: i })) ?? [],
},
noopener: { type: FormFieldType.Boolean },
nofollow: { type: FormFieldType.Boolean },
singleLine: { type: FormFieldType.Boolean },
export const ContentSuggestionField = 'ContentSuggestion';

export const linkComponentSchema: ContentComponentSchema<
ContentLinkComponent,
{ [ContentSuggestionField]: unknown }
> = {
name: 'Link',
group: 'Content',
icon: IconTypes.Link,
options: {
[ContentSuggestionField]: {
type: ContentSuggestionField,
width: 100,
},
url: { type: FormFieldType.Text },
target: {
type: FormFieldType.Select,
options: [
{
value: '_blank',
text: '_blank',
},
{
value: '_self',
text: '_self',
},
{
value: '_parent',
text: '_parent',
},
{
value: '_top',
text: '_top',
},
],
},
color: {
type: FormFieldType.Select,
options: [{ value: ColorType.Primary }, { value: ColorType.Neutral }],
},
icon: {
type: FormFieldType.Select,
options:
iconInjectable
.get()
?.getIcons()
.sort()
.map((i) => ({ value: i, text: i })) ?? [],
},
content: { text: { type: FormFieldType.Text } },
};
noopener: { type: FormFieldType.Boolean },
nofollow: { type: FormFieldType.Boolean },
singleLine: { type: FormFieldType.Boolean },
},
content: { text: { type: FormFieldType.Text } },
};
1 change: 1 addition & 0 deletions libs/domain/content/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@spryker-oryx/di": "1.0.2",
"@spryker-oryx/experience": "1.0.2",
"@spryker-oryx/router": "1.0.2",
"@spryker-oryx/product": "1.0.2",
"@spryker-oryx/site": "1.0.2",
"@spryker-oryx/utilities": "1.0.2"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class DefaultContentService implements ContentService {
protected contents: Record<string, string[]> = {};

constructor(
protected adapters = inject(ContentAdapter),
protected adapters = inject(ContentAdapter, [] as ContentAdapter[]),
protected config = inject(ContentConfig, [] as ContentConfig[]),
protected injector = inject(INJECTOR)
) {
Expand Down
3 changes: 1 addition & 2 deletions libs/domain/product/card/src/card.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { fixture } from '@open-wc/testing-helpers';
import { contentLinkComponent } from '@spryker-oryx/content';
import * as core from '@spryker-oryx/core';
import { createInjector, destroyInjector } from '@spryker-oryx/di';
import { mockProductProviders } from '@spryker-oryx/product/mocks';
Expand Down Expand Up @@ -34,7 +33,7 @@ describe('ProductCardComponent', () => {
let element: ProductCardComponent;

beforeAll(async () => {
await useComponent([productCardComponent, contentLinkComponent]);
await useComponent([productCardComponent]);
});

beforeEach(async () => {
Expand Down
1 change: 0 additions & 1 deletion libs/domain/product/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
}
},
"dependencies": {
"@spryker-oryx/content": "1.0.2",
"@spryker-oryx/core": "1.0.2",
"@spryker-oryx/di": "1.0.2",
"@spryker-oryx/experience": "1.0.2",
Expand Down
Loading
Loading