Skip to content

Commit

Permalink
feat(atomic): clickable "More matches for {query}" in facetSearch (#4154
Browse files Browse the repository at this point in the history
)

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

Still need to implement the feature for the commerce use case in a
future PR.

BEFORE
<img width="474" alt="image"
src="https://github.com/coveo/ui-kit/assets/78121423/319ad32b-ac0f-48d6-ab4a-419c02864575">

AFTER
<img width="485" alt="image"
src="https://github.com/coveo/ui-kit/assets/78121423/0fd849af-a6c9-43a0-bc22-7e13da714505">
increments like this :
numberOfValues += initalNumberOfValues 
<img width="480" alt="image"
src="https://github.com/coveo/ui-kit/assets/78121423/d9a3a5ba-828b-47e2-a449-87f049ddd448">
  • Loading branch information
alexprudhomme authored Jul 9, 2024
1 parent cd679cc commit a389b40
Show file tree
Hide file tree
Showing 17 changed files with 268 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import {FunctionalComponent, h} from '@stencil/core';
import escape from 'escape-html';
import {i18n} from 'i18next';
import {Button} from '../../button';

interface FacetSearchMatchesProps {
i18n: i18n;
query: string;
numberOfMatches: number;
hasMoreMatches: boolean;
showMoreMatches?: () => void;
}

//TODO: change to noMatchesFound & remove the key in https://coveord.atlassian.net/browse/KIT-3368
function matchesFound(
key: 'more-matches-for' | 'no-matches-found-for',
query: string,
Expand All @@ -22,13 +25,21 @@ function matchesFound(
});
}

function clickableMoreMatchesFound(query: string, i18n: i18n) {
return i18n.t('more-matches-for', {
query: `<span class="font-bold italic" part="matches-query">${escape(
query
)}</span>`,
interpolation: {escapeValue: false},
});
}

export const FacetSearchMatches: FunctionalComponent<
FacetSearchMatchesProps
> = (props) => {
if (!props.numberOfMatches) {
return (
<div class="px-2">
{/* file deepcode ignore ReactSetInnerHtml: This is not React code */}
<div
part="no-matches"
class="truncate p-3 bg-neutral-light text-neutral-dark text-sm rounded"
Expand All @@ -43,6 +54,23 @@ export const FacetSearchMatches: FunctionalComponent<
}

if (props.hasMoreMatches) {
if (props.showMoreMatches) {
return (
<div class="px-2">
<Button
style="text-primary"
class="mt-3 p-2"
onClick={props.showMoreMatches}
>
<div
part="more-matches"
class="truncate text-sm"
innerHTML={clickableMoreMatchesFound(props.query, props.i18n)}
></div>
</Button>
</div>
);
}
return (
<div class="px-2">
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
AriaLiveRegion,
FocusTargetController,
} from '../../../utils/accessibility-utils';
import {getFieldCaptions} from '../../../utils/field-utils';
import {
BindStateToController,
InitializableComponent,
Expand All @@ -31,11 +30,6 @@ import {FacetGuard} from '../../common/facets/facet-guard';
import {FacetHeader} from '../../common/facets/facet-header/facet-header';
import {FacetPlaceholder} from '../../common/facets/facet-placeholder/facet-placeholder';
import {announceFacetSearchResultsWithAriaLive} from '../../common/facets/facet-search/facet-search-aria-live';
import {FacetSearchInput} from '../../common/facets/facet-search/facet-search-input';
import {FacetSearchInputGuard} from '../../common/facets/facet-search/facet-search-input-guard';
import {FacetSearchMatches} from '../../common/facets/facet-search/facet-search-matches';
import {shouldDisplaySearchResults} from '../../common/facets/facet-search/facet-search-utils';
import {FacetSearchValue} from '../../common/facets/facet-search/facet-search-value';
import {FacetShowMoreLess} from '../../common/facets/facet-show-more-less/facet-show-more-less';
import {
FacetValueProps,
Expand All @@ -59,7 +53,6 @@ export class AtomicInsightFacet
@InitializeBindings() public bindings!: InsightBindings;
public facet!: InsightFacet;
public searchStatus!: InsightSearchStatus;
public withSearch = false;
public dependsOn = {};
@Element() private host!: HTMLElement;

Expand Down Expand Up @@ -211,32 +204,8 @@ export class AtomicInsightFacet
onToggleCollapse={() => (this.isCollapsed = !this.isCollapsed)}
headerRef={(el) => this.focusTargets.header.setTarget(el)}
></FacetHeader>
<FacetSearchInputGuard
canShowMoreValues={this.facetState.canShowMoreValues}
numberOfDisplayedValues={this.facetState.values.length}
withSearch={this.withSearch}
>
<FacetSearchInput
i18n={this.bindings.i18n}
label={this.label}
onChange={(value) => {
if (value === '') {
this.facet.facetSearch.clear();
return;
}
this.facet.facetSearch.updateCaptions(
getFieldCaptions(this.field, this.bindings.i18n)
);
this.facet.facetSearch.updateText(value);
this.facet.facetSearch.search();
}}
onClear={() => this.facet.facetSearch.clear()}
query={this.facetState.facetSearch.query}
/>
</FacetSearchInputGuard>
{shouldDisplaySearchResults(this.facetState.facetSearch)
? [this.renderSearchResults(), this.renderMatches()]
: [this.renderValues(), this.renderShowMoreLess()]}

{[this.renderValues(), this.renderShowMoreLess()]}
</FacetContainer>
) : (
<FacetPlaceholder
Expand Down Expand Up @@ -265,20 +234,6 @@ export class AtomicInsightFacet
);
}

private renderSearchResults() {
return this.renderValuesContainer(
this.facet.state.facetSearch.values.map((value) => (
<FacetSearchValue
{...this.facetValueProps}
facetCount={value.count}
onExclude={() => this.facet.facetSearch.exclude(value)}
onSelect={() => this.facet.facetSearch.select(value)}
facetValue={value.rawValue}
/>
))
);
}

private renderValues() {
return this.renderValuesContainer(
this.facet.state.values.map((value, i) => {
Expand Down Expand Up @@ -330,17 +285,6 @@ export class AtomicInsightFacet
);
}

private renderMatches() {
return (
<FacetSearchMatches
i18n={this.bindings.i18n}
query={this.facet.state.facetSearch.query}
numberOfMatches={this.facet.state.facetSearch.values.length}
hasMoreMatches={this.facet.state.facetSearch.moreValuesAvailable}
></FacetSearchMatches>
);
}

private get activeValues() {
return this.facet.state.values.filter(({state}) => state !== 'idle');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,12 @@ export const Default: Story = {
'attributes-sort-criteria': 'occurrences',
},
};

export const LowFacetValues: Story = {
tags: ['test'],
args: {
'attributes-field': 'geographicalhierarchy',
'attributes-number-of-values': 2,
'attributes-with-search': true,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ export class AtomicCategoryFacet implements InitializableComponent {
query={this.facetState.facetSearch.query}
numberOfMatches={this.facetState.facetSearch.values.length}
hasMoreMatches={this.facetState.facetSearch.moreValuesAvailable}
showMoreMatches={() => this.facet.facetSearch.showMoreResults()}
></FacetSearchMatches>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {test, expect} from './fixture';

test.describe('when clicking facet search "More matches for"', () => {
test.beforeEach(async ({facet}) => {
await facet.load({story: 'low-facet-values'});
});
test('should display an increasing number of matches', async ({facet}) => {
await facet.getFacetSearch.click();
await facet.getFacetSearch.pressSequentially('p');
await expect
.poll(async () => {
return await facet.getFacetValue.count();
})
.toBeGreaterThanOrEqual(2);

await facet.facetSearchMoreMatchesFor.click();
await expect
.poll(async () => {
return await facet.getFacetValue.count();
})
.toBeGreaterThanOrEqual(4);

await facet.facetSearchMoreMatchesFor.click();
await expect
.poll(async () => {
return await facet.getFacetValue.count();
})
.toBeGreaterThanOrEqual(6);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {test as base} from '@playwright/test';
import {
AxeFixture,
makeAxeBuilder,
} from '../../../../../../playwright-utils/base-fixture';
import {AtomicCategoryFacetPageObject as Facet} from './page-object';

type MyFixture = {
facet: Facet;
};

export const test = base.extend<MyFixture & AxeFixture>({
makeAxeBuilder,
facet: async ({page}, use) => {
await use(new Facet(page));
},
});

export {expect} from '@playwright/test';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type {Page} from '@playwright/test';
import {BasePageObject} from '../../../../../../playwright-utils/base-page-object';

export class AtomicCategoryFacetPageObject extends BasePageObject<'atomic-category-facet'> {
constructor(page: Page) {
super(page, 'atomic-category-facet');
}

get getFacetSearch() {
return this.page.getByLabel('Search');
}

get getFacetValue() {
return this.page.locator('[part="search-result"]');
}

get facetSearchMoreMatchesFor() {
return this.page.getByRole('button', {name: 'More matches for p'});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,12 @@ export const Default: Story = {
},
decorators: [facetDecorator],
};

export const LowFacetValues: Story = {
tags: ['test'],
args: {
'attributes-field': 'objecttype',
'attributes-number-of-values': 2,
},
decorators: [facetDecorator],
};
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ export class AtomicColorFacet implements InitializableComponent {
query={this.facetState.facetSearch.query}
numberOfMatches={this.facetState.facetSearch.values.length}
hasMoreMatches={this.facetState.facetSearch.moreValuesAvailable}
showMoreMatches={() => this.facet.facetSearch.showMoreResults()}
></FacetSearchMatches>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {test, expect} from './fixture';

test.describe('when clicking facet search "More matches for"', () => {
test.beforeEach(async ({facet}) => {
await facet.load({story: 'low-facet-values'});
});
test('should display an increasing number of matches', async ({facet}) => {
await facet.getFacetSearch.click();
await facet.getFacetSearch.pressSequentially('p');
await expect
.poll(async () => {
return await facet.getFacetValue.count();
})
.toBeGreaterThanOrEqual(2);

await facet.facetSearchMoreMatchesFor.click();
await expect
.poll(async () => {
return await facet.getFacetValue.count();
})
.toBeGreaterThanOrEqual(4);

await facet.facetSearchMoreMatchesFor.click();
await expect
.poll(async () => {
return await facet.getFacetValue.count();
})
.toBeGreaterThanOrEqual(6);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {test as base} from '@playwright/test';
import {
AxeFixture,
makeAxeBuilder,
} from '../../../../../../playwright-utils/base-fixture';
import {AtomicColorFacetPageObject as Facet} from './page-object';

type MyFixture = {
facet: Facet;
};

export const test = base.extend<MyFixture & AxeFixture>({
makeAxeBuilder,
facet: async ({page}, use) => {
await use(new Facet(page));
},
});

export {expect} from '@playwright/test';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type {Page} from '@playwright/test';
import {BasePageObject} from '../../../../../../playwright-utils/base-page-object';

export class AtomicColorFacetPageObject extends BasePageObject<'atomic-color-facet'> {
constructor(page: Page) {
super(page, 'atomic-color-facet');
}

get getFacetSearch() {
return this.page.getByLabel('Search');
}

get getFacetValue() {
return this.page.locator('[part="value-box"]');
}

get facetSearchMoreMatchesFor() {
return this.page.getByRole('button', {name: 'More matches for p'});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,12 @@ export const Default: Story = {
},
decorators: [facetDecorator],
};

export const LowFacetValues: Story = {
tags: ['test'],
args: {
'attributes-field': 'objecttype',
'attributes-number-of-values': 2,
},
decorators: [facetDecorator],
};
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ export class AtomicFacet implements InitializableComponent {
query={this.facet.state.facetSearch.query}
numberOfMatches={this.facet.state.facetSearch.values.length}
hasMoreMatches={this.facet.state.facetSearch.moreValuesAvailable}
showMoreMatches={() => this.facet.facetSearch.showMoreResults()}
></FacetSearchMatches>
);
}
Expand Down
Loading

0 comments on commit a389b40

Please sign in to comment.