Skip to content
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: 2 additions & 2 deletions packages/atomic/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ export namespace Components {
*/
"sortCriteria": InsightRangeFacetSortCriterion;
/**
* Whether this facet should contain an datepicker allowing users to set custom ranges.
* Whether this facet should contain a date picker allowing users to set custom ranges.
*/
"withDatePicker": boolean;
}
Expand Down Expand Up @@ -1459,7 +1459,7 @@ declare namespace LocalJSX {
*/
"sortCriteria"?: InsightRangeFacetSortCriterion;
/**
* Whether this facet should contain an datepicker allowing users to set custom ranges.
* Whether this facet should contain a date picker allowing users to set custom ranges.
*/
"withDatePicker"?: boolean;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/atomic/src/components/commerce/Introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ You can also find examples of full implementations below:
These stories provide example usages of the Coveo Atomic web components for Commerce.
Each of the components must be placed correctly within the component hierarchy in order to function.
The top level of the tree must match the interface being initialized by the Headless engine.
For example, most Coveo Commerce interfaces must start have `atomic-commerce-interface` at the top the component hierarchy.
For example, most Coveo Commerce interfaces must have `atomic-commerce-interface` at the top of the component hierarchy.

### Initialization
Here is an example of initializing a `atomic-commerce-interface`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ import {randomID} from '@/src/utils/utils';
* @part result-table - The table element when the display prop is set to "table".
* @part result-table-heading - The thead element when the display prop is set to "table".
* @part result-table-heading-row - The tr element nested under the thead when the display prop is set to "table".
* @part result-table-heading-cell - The th elements nested under thead > tr when the display prop is set to "table".
* @part result-table-heading-cell - The `th` elements nested under `thead` > `tr` when the display prop is set to "table".
* @part result-table-body - The tbody element when the display prop is set to "table".
* @part result-table-row - All tr elements nested under tbody when the display prop is set to "table".
* @part result-table-row-even - The even tr elements nested under tbody when the display prop is set to "table".
* @part result-table-row-odd - The odd tr elements nested under tbody when the display prop is set to "table".
* @part result-table-cell - The td elements nested under each tbody > tr when the display prop is set to "table".
* @part result-table-cell - The `td` elements nested under each `tbody` > `tr` when the display prop is set to "table".
*
* @slot default - The default slot where the product templates are defined.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class AtomicProductMultiValueText

/**
* The maximum number of field values to display.
* If there are _n_ more values than the specified maximum, the last displayed value will be "_n_ more...".
* If there are `n` more values than the specified maximum, the last displayed value will be "`n` more...".
*/
@property({reflect: true, type: Number, attribute: 'max-values-to-display'})
public maxValuesToDisplay = 3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {itemContext} from '@/src/components/common/item-list/stencil-item-decora
*
* This method is useful for building custom product template elements, see [Create a Product List](https://docs.coveo.com/en/atomic/latest/cc-search/create-custom-components/native-components/#custom-product-template-component-example) for more information.
*
* You should use the method in the [connectedCallback lifecycle method](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks).
* You should use the method in the [`connectedCallback` lifecycle method](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks).
*
* @param element - The element that the event is dispatched to, which must be the child of a rendered "atomic-product".
* @returns A promise that resolves on initialization of the parent "atomic-product" element, or rejects when there is no parent "atomic-product" element.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ export class AtomicLayoutSection extends LightDomMixin(LitElement) {

/**
* For column sections, the minimum horizontal space it should take.
* E.g. '300px'
* For example, '300px'
*/
@property({type: String, reflect: true, attribute: 'min-width'})
minWidth?: string;

/**
* For column sections, the maximum horizontal space it should take.
* E.g. '300px'
* For example, '300px'
*/
@property({type: String, reflect: true, attribute: 'max-width'})
maxWidth?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type {AtomicFocusTrap} from '../atomic-focus-trap/atomic-focus-trap.js';
import type {AnyBindings} from '../interface/bindings.js';

/**
* When the modal is opened, the class `atomic-modal-opened` is added to the interfaceElement and the body, allowing further customization.
* When the modal is opened, the class `atomic-modal-opened` is added to the `interfaceElement` and the body, allowing further customization.
*
* @part backdrop - The transparent backdrop hiding the content behind the modal.
* @part container - The modal's outermost container with the outline and background.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {deepEqual} from '@/src/utils/compare-utils';
* provided Bueno schema.
*
* It validates the props when the host is connected to the DOM and whenever
* the host updates, revalidating only if the props have changed since the last
* the host updates, re-validating only if the props have changed since the last
* validation.
*
* If validation fails, the controller either sets the `error` property on the host
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export class AtomicInsightTimeframeFacet
*/
@Prop({reflect: true}) public field = 'date';
/**
* Whether this facet should contain an datepicker allowing users to set custom ranges.
* Whether this facet should contain a date picker allowing users to set custom ranges.
*/
@Prop({reflect: true}) public withDatePicker = false;
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import {loadRecommendationActions} from '@coveo/headless/recommendation';
import {Component, Element, Fragment, h, Prop, State} from '@stencil/core';
import CloseIcon from '../../../images/close.svg';
import SearchIcon from '../../../images/search.svg';
import {
InitializableComponent,
InitializeBindings,
} from '../../../utils/initialization-utils';
import {Button} from '../../common/stencil-button';
import {Bindings} from '../../search/atomic-search-interface/atomic-search-interface';

const numberOrPixelValuePattern = new RegExp(/^(?=.*(?:\d+|px)$).*$/);

/**
* @internal
*/
@Component({
tag: 'atomic-ipx-button',
styleUrl: './atomic-ipx-button.pcss',
shadow: true,
})
export class AtomicIPXButton implements InitializableComponent {
@InitializeBindings() public bindings!: Bindings;

@State() public error!: Error;

@Element() public host!: HTMLElement;

/**
* The label that will be shown to the user.
*/
@Prop({reflect: true}) public label?: string;

/**
* The close icon of the button.
*/
@Prop({reflect: true}) public closeIcon = CloseIcon;

/**
* The open icon of the button.
*/
@Prop({reflect: true}) public openIcon = SearchIcon;

/**
* Whether the IPX modal is open.
*/
@Prop({mutable: true, reflect: true}) public isModalOpen = false;

private recommendationsLoaded = false;

private async getRecommendations() {
const recsEngine = this.bindings.interfaceElement.querySelector(
'atomic-recs-interface'
)?.engine;
if (recsEngine) {
this.recommendationsLoaded = true;
recsEngine.dispatch(
loadRecommendationActions(recsEngine).getRecommendations()
);
}
}

private async onClick() {
if (!this.recommendationsLoaded) {
this.getRecommendations();
}
this.isModalOpen ? this.close() : this.open();
this.render();
}

private renderIPXButton() {
return (
<Button
style="primary"
part="ipx-button"
class="my-2"
onClick={() => this.onClick()}
>
<span part="button-icon">
<atomic-icon
part="ipx-close-icon"
icon={this.getIcon(this.closeIcon)}
></atomic-icon>
<atomic-icon
part="ipx-open-icon"
icon={this.getIcon(this.openIcon)}
></atomic-icon>
</span>
{this.label ? <span part="button-text">{this.label}</span> : null}
</Button>
);
}

public render() {
const [displayedIcon, hiddenIcon] = this.isModalOpen
? ['ipx-close-icon', 'ipx-open-icon']
: ['ipx-open-icon', 'ipx-close-icon'];
if (this.isModalOpen && !this.recommendationsLoaded) {
this.getRecommendations();
}

return (
<Fragment>
{
<style>
{`
[part=${displayedIcon}] {
transform: translateY(0rem);
}

[part=${hiddenIcon}] {
transform: translateY(3rem);
}

.btn-open {
[part=${displayedIcon}] {
transform: translateY(3rem);
}

[part=${hiddenIcon}] {
transform: translateY(0rem);
}
}`}
</style>
}
<div class="flex flex-col items-center" part="container">
{this.renderIPXButton()}
</div>
</Fragment>
);
}

private get ipxModal() {
return this.bindings.interfaceElement.querySelector('atomic-ipx-modal')!;
}

private open() {
this.isModalOpen = true;
this.host.classList.add('btn-open');
this.ipxModal.setAttribute('is-open', 'true');
}

private close() {
this.isModalOpen = false;
this.host.classList.remove('btn-open');
this.ipxModal.setAttribute('is-open', 'false');
}

private getIcon(icon: string) {
const initialDiv = document.createElement('div')!;
initialDiv.innerHTML = icon;
const iconElement = initialDiv.querySelector('svg');
if (!iconElement) {
return initialDiv.innerHTML;
}
// here, we grab the icon width and height to set a `viewbox` (which keeps the svg looking normal), then remove styles from the icon to let the icon stretch into the space it is given.
const iconWidth = this.getIconWidth(iconElement);
const iconHeight = this.getIconHeight(iconElement);
this.cleanupSVGStyles(iconElement);
if (iconWidth && iconHeight) {
iconElement.setAttribute('viewBox', `0 0 ${iconWidth} ${iconHeight}`);
}
return initialDiv.innerHTML;
}

private cleanupSVGStyles(iconElement: SVGSVGElement) {
iconElement.removeAttribute('style');
iconElement.removeAttribute('width');
iconElement.removeAttribute('height');
}

private getIconWidth(icon: SVGSVGElement) {
const width = icon.getAttribute('width') ?? '';
if (numberOrPixelValuePattern.test(width)) {
return width;
}
return null;
}

private getIconHeight(icon: SVGSVGElement) {
const height = icon.getAttribute('height') ?? '';
if (numberOrPixelValuePattern.test(height)) {
return height;
}
return null;
}
}
4 changes: 2 additions & 2 deletions packages/atomic/src/components/search/Introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ For a detailed breakdown of how to configure a Search engine, please refer to th
getSampleSearchEngineConfiguration
} = await import('https://static.cloud.coveo.com/headless/v3/headless.esm.js');


await customElements.whenDefined('atomic-search-interface');
const searchInterface = document.querySelector('atomic-search-interface');
await searchInterface.initialize(getSampleSearchEngineConfiguration());
Expand Down Expand Up @@ -107,4 +106,5 @@ If you are looking to implement Coveo in a framework, you can find reference cod
* [NextJS - Search (using App router)](https://github.com/coveo/ui-kit/tree/main/samples/atomic/search-nextjs-app-router)
* [NextJS - Search (using Pages router)](https://github.com/coveo/ui-kit/tree/main/samples/atomic/search-nextjs-pages-router)
* [Stencil - Search](https://github.com/coveo/ui-kit/tree/main/samples/atomic/search-stencil)
* [Vue - Search](https://github.com/coveo/ui-kit/tree/main/samples/atomic/search-vuejs)
* [Vue - Search](https://github.com/coveo/ui-kit/tree/main/samples/atomic/search-vuejs)

Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,24 @@ export class AtomicFieldCondition
private resultController = createResultContextController(this);

/**
* A condition that is satisfied when the specified field is defined on a result (e.g., `if-defined="author"` is satisfied when a result has the `author` field).
* A condition that is satisfied when the specified field is defined on a result (for example, `if-defined="author"` is satisfied when a result has the `author` field).
*/
@property({type: String, attribute: 'if-defined'}) ifDefined?: string;

/**
* A condition that is satisfied when the specified field is not defined on a result (e.g., `if-not-defined="author"` is satisfied when a result does not have the `author` field).
* A condition that is satisfied when the specified field is not defined on a result (for example, `if-not-defined="author"` is satisfied when a result does not have the `author` field).
*/
@property({type: String, attribute: 'if-not-defined'}) ifNotDefined?: string;

/**
* A condition that is satisfied when the specified field matches one of the specified values on a result (e.g., `must-match-filetype="pdf,docx"` is satisfied when a result has a filetype of either pdf or docx).
* A condition that is satisfied when the specified field matches one of the specified values on a result (for example, `must-match-filetype="pdf,docx"` is satisfied when a result has a filetype of either pdf or docx).
* @type {Record<string, string[]>}
*/
@mapProperty({splitValues: true, attributePrefix: 'must-match'})
mustMatch!: Record<string, string[]>;

/**
* A condition that is satisfied when the specified field does not match any of the specified values on a result (e.g., `must-not-match-filetype="pdf"` is satisfied when a result does not have a filetype of pdf).
* A condition that is satisfied when the specified field does not match any of the specified values on a result (for example, `must-not-match-filetype="pdf"` is satisfied when a result does not have a filetype of pdf).
* @type {Record<string, string[]>}
*/
@mapProperty({splitValues: true, attributePrefix: 'must-not-match'})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class AtomicResultMultiValueText

/**
* The maximum number of field values to display.
* If there are _n_ more values than the specified maximum, the last displayed value will be "_n_ more...".
* If there are `n` more values than the specified maximum, the last displayed value will be "`n` more...".
*/
@property({type: Number, reflect: true, attribute: 'max-values-to-display'})
public maxValuesToDisplay = 3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {ItemSectionMixin} from '@/src/mixins/item-section-mixin';
*
* Behavior:
* * Has a maximum height of two lines.
* ** We recommend that you use `atomic-result-fields-list` to ensure that the fields in this section dont overflow.
* ** We recommend that you use `atomic-result-fields-list` to ensure that the fields in this section don't overflow.
* * Exposes the `--line-height` variable so child elements can adjust to the current line height.
* * Has a defined CSS `color` property for text.
* * Has a font weight.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function findHighlightedElements(iframe?: HTMLIFrameElement): HTMLElement[] {

// Collect tagged word elements as well as elements whose id starts with
// the highlight prefix. Tagged words (`<coveotaggedword>`) must be
// considered so that invalid identifiers (e.g. id="invalid") are
// considered so that invalid identifiers (for example id="invalid") are
// discovered and cause the appropriate error in the parser.
const selector = `[id^="${HIGHLIGHT_PREFIX}"]`;
const query = root.querySelectorAll<HTMLElement>(selector);
Expand Down
Loading