Skip to content

Commit

Permalink
Add support for removable filters
Browse files Browse the repository at this point in the history
- Update AppliedFilters to have a `removable` field. If true, applied static filters and facets will be removable and will trigger a new search when removed.
- CSS styling for removable filters
- TODO: add jest tests

J=SLAP-1641
TEST=manual

spin up sample app, search 'virginia', and click on static filter and facet options. See that when removable option is true, the applied filter labels have 'X' button. When click, see that the corresponding label is removed and the result is properly updated
  • Loading branch information
Yen Truong committed Oct 22, 2021
1 parent 0e09edc commit c0481d0
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 12 deletions.
60 changes: 50 additions & 10 deletions sample-app/src/components/AppliedFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
import { DisplayableFilter } from '../models/displayableFilter';
import { GroupedFilters } from '../models/groupedFilters';
import '../sass/AppliedFilters.scss';
import { ReactComponent as CloseX } from '../icons/x.svg';
import { useAnswersActions } from '@yext/answers-headless-react'
import { isNearFilterValue } from '../utils/filterutils';

interface Props {
showFieldNames?: boolean,
labelText?: string,
delimiter?: string,
removable?: boolean,
appliedFilters: Array<GroupedFilters>
}

/**
* Renders AppliedFilters component
*/
export default function AppliedFilters({ showFieldNames, labelText, delimiter, appliedFilters }: Props): JSX.Element {
export default function AppliedFilters({
showFieldNames,
labelText,
delimiter,
appliedFilters,
removable = false
}: Props): JSX.Element {
return (
<div className="AppliedFilters" aria-label={labelText}>
{appliedFilters.map((filterGroup: GroupedFilters, index: number) => {
return (
<div className="AppliedFilters__filterGroup" key={filterGroup.label}>
{showFieldNames && renderFilterLabel(filterGroup.label)}
{renderAppliedFilters(filterGroup.filters)}
{renderAppliedFilters(filterGroup.filters, removable)}
{index < appliedFilters.length - 1 && <div className="AppliedFilters__filterSeparator">{delimiter}</div>}
</div>
);
Expand All @@ -37,14 +47,44 @@ function renderFilterLabel(label: string): JSX.Element {
);
}

function renderAppliedFilters(filters: Array<DisplayableFilter>): JSX.Element {
function renderAppliedFilters(filters: Array<DisplayableFilter>, removable: boolean): JSX.Element {
const filterElems = filters.map((filter: DisplayableFilter, index: number) => {
return (
<div className="AppliedFilters__filterValue" key={filter.label}>
<span className="AppliedFilters__filterValueText">{filter.label}</span>
{index < filters.length - 1 && <span className="AppliedFilters__filterValueComma">,</span>}
</div>
);
if (filter.filterType === 'NLP_FILTER' || !removable) {
return (
<div className="AppliedFilters__filterValue" key={filter.label}>
<span className="AppliedFilters__filterValueText">{filter.label}</span>
{index < filters.length - 1 && <span className="AppliedFilters__filterValueComma">,</span>}
</div>
);
}
return <RemovableFilter filter={filter} key={filter.label}/>
});

return <>{filterElems}</>;
}
}

function RemovableFilter({ filter }: {filter: DisplayableFilter }): JSX.Element {
const answersAction = useAnswersActions();

const onRemoveFacetOption = () => {
const { fieldId, matcher, value } = filter.filter;
if (isNearFilterValue(value)) {
throw Error('unrecognized value type for facet option.');
}
answersAction.unselectFacetOption(fieldId, { matcher, value });
answersAction.executeVerticalQuery();
}

const onRemoveStaticFilterOption = () => {
document.getElementById(`${filter.filter.fieldId + "_" + filter.filter.value}`)?.click();
}

let onRemoveFilter = filter.filterType === 'FACET' ? onRemoveFacetOption : onRemoveStaticFilterOption;

return (
<div className="AppliedFilters__filterValue AppliedFilters__removableFilter">
<span className="AppliedFilters__filterValueText">{filter.label}</span>
<div className='AppliedFilters__removeFilterButton' onClick={onRemoveFilter}><CloseX/></div>
</div>
);
}
1 change: 1 addition & 0 deletions sample-app/src/components/DecoratedAppliedFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface DecoratedAppliedFiltersConfig {
hiddenFields?: Array<string>,
labelText?: string,
delimiter?: string,
removable?: boolean,
appliedQueryFilters?: AppliedQueryFilter[]
}

Expand Down
3 changes: 3 additions & 0 deletions sample-app/src/icons/x.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions sample-app/src/pages/VerticalSearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export default function VerticalSearchPage(props: {
showFieldNames={true}
hiddenFields={['builtin.entityType']}
delimiter='|'
removable={true}
/>
<AlternativeVerticals
currentVerticalLabel='People'
Expand Down
22 changes: 22 additions & 0 deletions sample-app/src/sass/AppliedFilters.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
display: flex;
}

&__filterLabel,
&__filterValueComma {
margin-right: 5px;
}

&__filterValue {
display: flex;
}

&__filterSeparator {
margin: 0 10px;
}
Expand All @@ -16,5 +25,18 @@
&__filterValueComma {
font-style: italic;
}

&__removeFilterButton {
margin: 0 5px;
width: .5625rem;
cursor: pointer;
}

&__removableFilter {
margin: 0 5px;
display: flex;
justify-content: center;
background-color: #EFEFEF;
}
}

4 changes: 2 additions & 2 deletions sample-app/src/utils/filterutils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { NearFilterValue, CombinedFilter, Filter } from '@yext/answers-core';
/**
* Check if the object follows NearFilterValue interface
*/
export function isNearFilterValue(obj: Object): obj is NearFilterValue {
return 'radius' in obj && 'lat' in obj && 'long' in obj;
export function isNearFilterValue(obj: any): obj is NearFilterValue {
return typeof obj === 'object' && 'radius' in obj && 'lat' in obj && 'long' in obj;
}

/**
Expand Down

0 comments on commit c0481d0

Please sign in to comment.