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

[ENG-4656] Special-case is-present type filters #1994

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
6 changes: 6 additions & 0 deletions app/models/related-property-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ interface PropertyPath {
shortFormLabel: LanguageText[];
}

export enum SuggestedFilterOperators {
AnyOf = 'any-of',
IsPresent = 'is-present'
}

export default class RelatedPropertyPathModel extends OsfModel {
@attr('string') propertyPathKey!: string;
@attr('number') cardSearchResultCount!: number;
@attr('array') osfmapPropertyPath!: string[];
@attr('array') propertyPath!: PropertyPath[];
@attr('string') suggestedFilterOperator!: SuggestedFilterOperators;

getLocalizedString = new GetLocalizedPropertyHelper(getOwner(this));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import IntlService from 'ember-intl/services/intl';
import RelatedPropertyPathModel from 'ember-osf-web/models/related-property-path';

import { Filter } from '../component';

interface BooleanFiltersArgs {
cardSearchText: string;
cardSearchFilter: Filter[];
properties: RelatedPropertyPathModel[];
toggleFilter: (filter: Filter) => void;
}

export default class BooleanFilters extends Component<BooleanFiltersArgs> {
@service intl!: IntlService;

@tracked collapsed = true;

get visibleLabel() {
return this.intl.t('search.boolean-filters.dropdown-label');
}

get hasFilterableValues() {
return this.args.properties.some(property => property.cardSearchResultCount > 0);
}

get filterableValues() {
return this.args.properties.filterBy('cardSearchResultCount').sortBy('cardSearchResultCount').reverse();
}

@action
toggleFacet() {
this.collapsed = !this.collapsed;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Styles derived from <SearchPage::FilterFacet> component

.facet-wrapper {
padding: 0.5rem 0;
}

.facet-wrapper:not(:first-of-type) {
border-top: 1px solid $color-border-gray;
}

.facet-expand-button {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;

&:active {
box-shadow: none;
}
}

.facet-list {
list-style: none;
max-height: 300px;
overflow-y: auto;
margin: 0;
padding: 0.2rem;

&.collapsed {
display: none;
}
}

.facet-value {
display: flex;
justify-content: space-between;
margin: 10px 0;

.facet-link {
margin: 0 5px;
max-width: 90%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}

.facet-count {
flex-shrink: 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{{#if this.hasFilterableValues}}
<div
data-analytics-scope='Search page boolean filters wrapper'
data-test-boolean-filter-list
local-class='facet-wrapper'
...attributes
>
{{#let (unique-id 'boolean-filters') as |facetElementId|}}
<Button
data-analytics-name='Toggle boolean filters'
data-test-filter-boolean-filters
@layout='fake-link'
local-class='facet-expand-button'
aria-controls={{facetElementId}}
aria-expanded={{not this.collapsed}}
{{on 'click' this.toggleFacet}}
>
<span>{{t 'search.boolean-filters.dropdown-label'}}</span>
<FaIcon @icon={{if this.collapsed 'caret-down' 'caret-up'}} />
</Button>
<ul
local-class='facet-list {{if this.collapsed 'collapsed'}}'
id={{facetElementId}}
>
{{#each this.filterableValues as |value|}}
<li
data-test-filter-boolean-value={{value.displayLabel}}
local-class='facet-value'
>
<Button
data-analytics-name='Toggle boolean value {{value.displayLabel}}'
local-class='facet-link'
@layout='fake-link'
title={{value.displayLabel}}
{{on 'click' (fn @toggleFilter (hash
propertyVisibleLabel=this.visibleLabel
propertyShortFormLabel=value.propertyPathKey
label=value.displayLabel
value='is-present'
suggestedFilterOperator='is-present'
)
)}}
>
{{value.displayLabel}}
</Button>
<span local-class='facet-count'>
{{value.cardSearchResultCount}}
</span>
</li>
{{/each}}
</ul>
{{/let}}
</div>
{{/if}}
13 changes: 8 additions & 5 deletions lib/osf-components/addon/components/search-page/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ import Media from 'ember-responsive';
import { ShareMoreThanTenThousand } from 'ember-osf-web/models/index-card-search';
import SearchResultModel from 'ember-osf-web/models/search-result';
import ProviderModel from 'ember-osf-web/models/provider';
import RelatedPropertyPathModel from 'ember-osf-web/models/related-property-path';
import RelatedPropertyPathModel, { SuggestedFilterOperators } from 'ember-osf-web/models/related-property-path';
import uniqueId from 'ember-osf-web/utils/unique-id';

import { booleanFilterProperties } from './filter-facet/component';

interface ResourceTypeOption {
display: string;
value?: ResourceTypeFilterValue | null;
Expand All @@ -42,6 +40,7 @@ export interface Filter {
propertyShortFormLabel: string; // OSFMAP shorthand label
value: string;
label: string;
suggestedFilterOperator?: SuggestedFilterOperators;
}

export interface OnSearchParams {
Expand Down Expand Up @@ -76,6 +75,7 @@ export default class SearchPage extends Component<SearchArgs> {
@tracked cardSearchText?: string;
@tracked searchResults?: SearchResultModel[];
@tracked relatedProperties?: RelatedPropertyPathModel[] = [];
@tracked booleanFilters?: RelatedPropertyPathModel[] = [];
@tracked page?: string = '';
@tracked totalResultCount?: string | number;
@tracked firstPageCursor?: string | null;
Expand Down Expand Up @@ -199,7 +199,7 @@ export default class SearchPage extends Component<SearchArgs> {
const { page, sort, activeFilters, resourceType } = this;
let filterQueryObject = activeFilters.reduce((acc, filter) => {
// boolean filters should look like cardSearchFilter[hasDataResource][is-present]
if (booleanFilterProperties.includes(filter.propertyShortFormLabel)) {
if (filter.suggestedFilterOperator === SuggestedFilterOperators.IsPresent) {
acc[filter.propertyShortFormLabel] = {};
acc[filter.propertyShortFormLabel][filter.value] = true;
return acc;
Expand All @@ -225,7 +225,10 @@ export default class SearchPage extends Component<SearchArgs> {
'page[size]': 10,
});
await searchResult.relatedProperties;
this.relatedProperties = searchResult.relatedProperties;
this.booleanFilters = searchResult.relatedProperties
.filterBy('suggestedFilterOperator', SuggestedFilterOperators.IsPresent);
this.relatedProperties = searchResult.relatedProperties
.filterBy('suggestedFilterOperator', SuggestedFilterOperators.AnyOf);
this.firstPageCursor = searchResult.firstPageCursor;
this.nextPageCursor = searchResult.nextPageCursor;
this.prevPageCursor = searchResult.prevPageCursor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,6 @@ interface FilterFacetArgs {

const searchDebounceTime = 500;

export const booleanFilterProperties = [
'hasAnalyticCodeResource', // registrations
'hasMaterialsResource', // registrations
'hasPapersResource', // registrations
'hasSupplementalResource', // registrations
'hasDataResource', // registrations and preprints
'hasPreregisteredAnalysisPlan', // preprints
'hasPreregisteredStudyDesign', // preprints
'isSupplementedBy', // preprints
'supplements', // projects
];

export default class FilterFacet extends Component<FilterFacetArgs> {
@service store!: Store;
@service intl!: IntlService;
Expand Down Expand Up @@ -118,22 +106,7 @@ export default class FilterFacet extends Component<FilterFacetArgs> {
async fetchFacetValues() {
const { cardSearchText, cardSearchFilter, property } = this.args;
const { page, sort, filterString } = this;
// If the property is a boolean filter (e.g. hasDataResource), we don't want to fetch IRI values
// SHARE API filters on these properties using:
// `share.osf.io/api/v3/index-card-search?cardSearchFilter[hasDataResource][is-present]`
// or cardSearchFilter[hasDataResource][is-absent] (although this one is not used in the app)
if (booleanFilterProperties.includes(property.shortFormLabel)) {
this.filterableValues = [
{
resourceId: 'is-present',
indexCard: {
label: this.intl.t('search.filter-facet.has-resource', { resource: property.displayLabel }),
resourceId: 'is-present',
},
},
];
return;
}

const valueSearch = await this.store.queryRecord('index-value-search', {
cardSearchText,
cardSearchFilter,
Expand Down
13 changes: 11 additions & 2 deletions lib/osf-components/addon/components/search-page/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,20 @@ as |layout|>
@property={{filterableProperty}}
@toggleFilter={{this.toggleFilter}}
/>
{{else}}
{{/each}}
{{#if this.booleanFilters.length}}
<SearchPage::BooleanFilters
@cardSearchText={{@cardSearchText}}
@cardSearchFilter={{this.filterQueryObject}}
@properties={{this.booleanFilters}}
@toggleFilter={{this.toggleFilter}}
/>
{{/if}}
{{#if (and (not this.booleanFilters.length) (not this.relatedProperties.length))}}
<p local-class='no-properties'>
{{t 'search.left-panel.no-filterable-properties'}}
</p>
{{/each}}
{{/if}}
{{/if}}
</layout.left>
<layout.main local-class={{if this.showSidePanelToggle 'search-main-mobile' 'search-main'}} data-analytics-scope='Search page main'>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'osf-components/components/search-page/boolean-filters/component';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'osf-components/components/search-page/boolean-filters/template';
3 changes: 3 additions & 0 deletions mirage/views/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ export function cardSearch(_: Schema, __: Request) {
],
},
],
suggestedFilterOperator: 'any-of',
},
},
{
Expand Down Expand Up @@ -369,6 +370,7 @@ export function cardSearch(_: Schema, __: Request) {
],
},
],
suggestedFilterOperator: 'any-of',
},
},
{
Expand Down Expand Up @@ -405,6 +407,7 @@ export function cardSearch(_: Schema, __: Request) {
],
},
],
suggestedFilterOperator: 'any-of',
},
},
],
Expand Down
3 changes: 2 additions & 1 deletion translations/en-us.yml
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,9 @@ search:
see-more-modal-text: 'Please select a filter to apply to your search.'
see-more-modal-placeholder: 'Search for a filter to apply'
load-more: 'Load more'
has-resource: 'Has {resource}'
no-resource: 'No {resource}'
boolean-filters:
dropdown-label: 'Has related resource'

institutions:
institution-logo: 'Logo for '
Expand Down
Loading