Skip to content

Commit

Permalink
feat(atomic): support highlights in atomic-product-description (#4541)
Browse files Browse the repository at this point in the history
This PR adds support for highlights in atomic-product-description.
Highlights are only supported when using the excerpt field.

https://coveord.atlassian.net/browse/KIT-3307

---------

Co-authored-by: GitHub Actions Bot <>
Co-authored-by: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com>
Co-authored-by: Frederic Beaudoin <fbeaudoin@coveo.com>
  • Loading branch information
3 people authored Oct 28, 2024
1 parent 1143cae commit 5c235a8
Show file tree
Hide file tree
Showing 21 changed files with 1,047 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ AtomicPopover,
AtomicProduct,
AtomicProductChildren,
AtomicProductDescription,
AtomicProductExcerpt,
AtomicProductFieldCondition,
AtomicProductImage,
AtomicProductLink,
Expand Down Expand Up @@ -205,6 +206,7 @@ AtomicPopover,
AtomicProduct,
AtomicProductChildren,
AtomicProductDescription,
AtomicProductExcerpt,
AtomicProductFieldCondition,
AtomicProductImage,
AtomicProductLink,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1278,14 +1278,14 @@ export declare interface AtomicProductChildren extends Components.AtomicProductC


@ProxyCmp({
inputs: ['field', 'truncateAfter']
inputs: ['field', 'isCollapsible', 'truncateAfter']
})
@Component({
selector: 'atomic-product-description',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['field', 'truncateAfter'],
inputs: ['field', 'isCollapsible', 'truncateAfter'],
})
export class AtomicProductDescription {
protected el: HTMLElement;
Expand All @@ -1299,6 +1299,28 @@ export class AtomicProductDescription {
export declare interface AtomicProductDescription extends Components.AtomicProductDescription {}


@ProxyCmp({
inputs: ['isCollapsible', 'truncateAfter']
})
@Component({
selector: 'atomic-product-excerpt',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['isCollapsible', 'truncateAfter'],
})
export class AtomicProductExcerpt {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}


export declare interface AtomicProductExcerpt extends Components.AtomicProductExcerpt {}


@ProxyCmp({
inputs: ['ifDefined', 'ifNotDefined', 'mustMatch', 'mustNotMatch']
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const AtomicNumericRange = /*@__PURE__*/createReactComponent<JSX.AtomicNu
export const AtomicProduct = /*@__PURE__*/createReactComponent<JSX.AtomicProduct, HTMLAtomicProductElement>('atomic-product');
export const AtomicProductChildren = /*@__PURE__*/createReactComponent<JSX.AtomicProductChildren, HTMLAtomicProductChildrenElement>('atomic-product-children');
export const AtomicProductDescription = /*@__PURE__*/createReactComponent<JSX.AtomicProductDescription, HTMLAtomicProductDescriptionElement>('atomic-product-description');
export const AtomicProductExcerpt = /*@__PURE__*/createReactComponent<JSX.AtomicProductExcerpt, HTMLAtomicProductExcerptElement>('atomic-product-excerpt');
export const AtomicProductFieldCondition = /*@__PURE__*/createReactComponent<JSX.AtomicProductFieldCondition, HTMLAtomicProductFieldConditionElement>('atomic-product-field-condition');
export const AtomicProductImage = /*@__PURE__*/createReactComponent<JSX.AtomicProductImage, HTMLAtomicProductImageElement>('atomic-product-image');
export const AtomicProductLink = /*@__PURE__*/createReactComponent<JSX.AtomicProductLink, HTMLAtomicProductLinkElement>('atomic-product-link');
Expand Down
55 changes: 53 additions & 2 deletions packages/atomic/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { InsightResultActionClickedEvent } from "./components/insight/atomic-ins
import { Section } from "./components/common/atomic-layout-section/sections";
import { AtomicCommonStore, AtomicCommonStoreData } from "./components/common/interface/store";
import { SelectChildProductEventArgs } from "./components/commerce/product-template-components/atomic-product-children/atomic-product-children";
import { TruncateAfter } from "./components/common/expandable-text/expandable-text";
import { RecommendationEngine } from "@coveo/headless/recommendation";
import { InteractiveResult as RecsInteractiveResult, LogLevel as RecsLogLevel, Result as RecsResult, ResultTemplate as RecsResultTemplate, ResultTemplateCondition as RecsResultTemplateCondition } from "./components/recommendations";
import { RecsInitializationOptions } from "./components/recommendations/atomic-recs-interface/atomic-recs-interface";
Expand Down Expand Up @@ -58,6 +59,7 @@ export { InsightResultActionClickedEvent } from "./components/insight/atomic-ins
export { Section } from "./components/common/atomic-layout-section/sections";
export { AtomicCommonStore, AtomicCommonStoreData } from "./components/common/interface/store";
export { SelectChildProductEventArgs } from "./components/commerce/product-template-components/atomic-product-children/atomic-product-children";
export { TruncateAfter } from "./components/common/expandable-text/expandable-text";
export { RecommendationEngine } from "@coveo/headless/recommendation";
export { InteractiveResult as RecsInteractiveResult, LogLevel as RecsLogLevel, Result as RecsResult, ResultTemplate as RecsResultTemplate, ResultTemplateCondition as RecsResultTemplateCondition } from "./components/recommendations";
export { RecsInitializationOptions } from "./components/recommendations/atomic-recs-interface/atomic-recs-interface";
Expand Down Expand Up @@ -2072,10 +2074,27 @@ export namespace Components {
* The name of the description field to use.
*/
"field": 'ec_description' | 'ec_shortdesc';
/**
* Whether the description should be collapsible after being expanded.
*/
"isCollapsible": boolean;
/**
* The number of lines after which the product description should be truncated. A value of "none" will disable truncation.
*/
"truncateAfter": 'none' | '1' | '2' | '3' | '4';
"truncateAfter": TruncateAfter;
}
/**
* @alpha The `atomic-product-excerpt` component renders the excerpt of a product generated at query time.
*/
interface AtomicProductExcerpt {
/**
* Whether the excerpt should be collapsible after being expanded.
*/
"isCollapsible": boolean;
/**
* The number of lines after which the product excerpt should be truncated. A value of "none" will disable truncation.
*/
"truncateAfter": TruncateAfter;
}
/**
* The `atomic-product-field-condition` component takes a list of conditions that, if fulfilled, apply the template in which it's defined.
Expand Down Expand Up @@ -4909,6 +4928,15 @@ declare global {
prototype: HTMLAtomicProductDescriptionElement;
new (): HTMLAtomicProductDescriptionElement;
};
/**
* @alpha The `atomic-product-excerpt` component renders the excerpt of a product generated at query time.
*/
interface HTMLAtomicProductExcerptElement extends Components.AtomicProductExcerpt, HTMLStencilElement {
}
var HTMLAtomicProductExcerptElement: {
prototype: HTMLAtomicProductExcerptElement;
new (): HTMLAtomicProductExcerptElement;
};
/**
* The `atomic-product-field-condition` component takes a list of conditions that, if fulfilled, apply the template in which it's defined.
* The condition properties can be based on any top-level product property of the `product` object, not restricted to fields (e.g., `ec_name`).
Expand Down Expand Up @@ -6061,6 +6089,7 @@ declare global {
"atomic-product": HTMLAtomicProductElement;
"atomic-product-children": HTMLAtomicProductChildrenElement;
"atomic-product-description": HTMLAtomicProductDescriptionElement;
"atomic-product-excerpt": HTMLAtomicProductExcerptElement;
"atomic-product-field-condition": HTMLAtomicProductFieldConditionElement;
"atomic-product-image": HTMLAtomicProductImageElement;
"atomic-product-link": HTMLAtomicProductLinkElement;
Expand Down Expand Up @@ -8080,10 +8109,27 @@ declare namespace LocalJSX {
* The name of the description field to use.
*/
"field"?: 'ec_description' | 'ec_shortdesc';
/**
* Whether the description should be collapsible after being expanded.
*/
"isCollapsible"?: boolean;
/**
* The number of lines after which the product description should be truncated. A value of "none" will disable truncation.
*/
"truncateAfter"?: 'none' | '1' | '2' | '3' | '4';
"truncateAfter"?: TruncateAfter;
}
/**
* @alpha The `atomic-product-excerpt` component renders the excerpt of a product generated at query time.
*/
interface AtomicProductExcerpt {
/**
* Whether the excerpt should be collapsible after being expanded.
*/
"isCollapsible"?: boolean;
/**
* The number of lines after which the product excerpt should be truncated. A value of "none" will disable truncation.
*/
"truncateAfter"?: TruncateAfter;
}
/**
* The `atomic-product-field-condition` component takes a list of conditions that, if fulfilled, apply the template in which it's defined.
Expand Down Expand Up @@ -9801,6 +9847,7 @@ declare namespace LocalJSX {
"atomic-product": AtomicProduct;
"atomic-product-children": AtomicProductChildren;
"atomic-product-description": AtomicProductDescription;
"atomic-product-excerpt": AtomicProductExcerpt;
"atomic-product-field-condition": AtomicProductFieldCondition;
"atomic-product-image": AtomicProductImage;
"atomic-product-link": AtomicProductLink;
Expand Down Expand Up @@ -10251,6 +10298,10 @@ declare module "@stencil/core" {
* @alpha The `atomic-product-description` component renders the description of a product.
*/
"atomic-product-description": LocalJSX.AtomicProductDescription & JSXBase.HTMLAttributes<HTMLAtomicProductDescriptionElement>;
/**
* @alpha The `atomic-product-excerpt` component renders the excerpt of a product generated at query time.
*/
"atomic-product-excerpt": LocalJSX.AtomicProductExcerpt & JSXBase.HTMLAttributes<HTMLAtomicProductExcerptElement>;
/**
* The `atomic-product-field-condition` component takes a list of conditions that, if fulfilled, apply the template in which it's defined.
* The condition properties can be based on any top-level product property of the `product` object, not restricted to fields (e.g., `ec_name`).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {wrapInCommerceInterface} from '@coveo/atomic-storybook-utils/commerce/commerce-interface-wrapper';
import {wrapInCommerceProductList} from '@coveo/atomic-storybook-utils/commerce/commerce-product-list-wrapper';
import {wrapInProductTemplate} from '@coveo/atomic-storybook-utils/commerce/commerce-product-template-wrapper';
import {parameters} from '@coveo/atomic-storybook-utils/common/common-meta-parameters';
import {renderComponent} from '@coveo/atomic-storybook-utils/common/render-component';
import type {Meta, StoryObj as Story} from '@storybook/web-components';
import {updateQuery} from '../../../../../../headless/src/features/commerce/query/query-actions';

const {
decorator: commerceInterfaceDecorator,
play: initializeCommerceInterface,
} = wrapInCommerceInterface({
skipFirstSearch: true,
});

const {decorator: commerceProductListDecorator} = wrapInCommerceProductList();
const {decorator: productTemplateDecorator} = wrapInProductTemplate();

const meta: Meta = {
component: 'atomic-product-description',
title: 'Atomic-Commerce/Product Template Components/ProductDescription',
id: 'atomic-product-description',
render: renderComponent,
parameters,
argTypes: {
'attributes-truncate-after': {
name: 'truncate-after',
type: 'string',
},
'attributes-field': {
name: 'field',
type: 'string',
},
'attributes-is-collapsible': {
name: 'is-collapsible',
type: 'boolean',
},
},
};

export default meta;

export const Default: Story = {
name: 'atomic-product-description',
parameters: {
viewport: {
defaultViewport: 'mobile1',
},
},
decorators: [
productTemplateDecorator,
commerceProductListDecorator,
commerceInterfaceDecorator,
],
play: async (context) => {
await initializeCommerceInterface(context);

const searchInterface = context.canvasElement.querySelector(
'atomic-commerce-interface'
);
searchInterface?.engine?.dispatch(updateQuery({query: 'kayak'}));

await searchInterface!.executeFirstRequest();
},
args: {
'attributes-field': 'ec_description',
},
};

This file was deleted.

Loading

0 comments on commit 5c235a8

Please sign in to comment.