Skip to content

Commit 57a6790

Browse files
committed
Merge branch 'release/23.10.0'
2 parents 6a0a8d3 + a8a0c87 commit 57a6790

File tree

119 files changed

+4490
-618
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+4490
-618
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [23.10.0] - 2023-08-28
8+
### Added
9+
- Added search improvement routes and related components
10+
711
## [23.09.0] - 2023-08-16
812
### Changed
913
- Added Google Tag Manager
@@ -1936,6 +1940,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
19361940
### Added
19371941
- Quick Files
19381942

1943+
[23.10.0]: https://github.com/CenterForOpenScience/ember-osf-web/releases/tag/23.10.0
19391944
[23.09.0]: https://github.com/CenterForOpenScience/ember-osf-web/releases/tag/23.09.0
19401945
[23.08.0]: https://github.com/CenterForOpenScience/ember-osf-web/releases/tag/23.08.0
19411946
[23.07.0]: https://github.com/CenterForOpenScience/ember-osf-web/releases/tag/23.07.0

app/adapters/index-card-search.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import ShareAdapter from './share-adapter';
2+
export default class IndexCardSearchAdapter extends ShareAdapter {
3+
pathForType() {
4+
return 'index-card-search';
5+
}
6+
}
7+
8+
declare module 'ember-data/types/registries/adapter' {
9+
export default interface AdapterRegistry {
10+
'index-card-search': IndexCardSearchAdapter;
11+
} // eslint-disable-line semi
12+
}

app/adapters/index-card.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import ShareAdapter from './share-adapter';
2+
3+
export default class IndexCardAdapter extends ShareAdapter {
4+
pathForType() {
5+
return 'index-card';
6+
}
7+
}
8+
9+
declare module 'ember-data/types/registries/adapter' {
10+
export default interface AdapterRegistry {
11+
'index-card': IndexCardAdapter;
12+
} // eslint-disable-line semi
13+
}

app/adapters/index-value-search.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import ShareAdapter from './share-adapter';
2+
3+
export default class IndexValueSearchAdapter extends ShareAdapter {
4+
pathForType() {
5+
return 'index-value-search';
6+
}
7+
}
8+
9+
declare module 'ember-data/types/registries/adapter' {
10+
export default interface AdapterRegistry {
11+
'index-value-search': IndexValueSearchAdapter;
12+
} // eslint-disable-line semi
13+
}

app/adapters/related-property-path.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import ShareAdapter from './share-adapter';
2+
3+
export default class RelatedPropertyPathAdapter extends ShareAdapter {
4+
}
5+
6+
declare module 'ember-data/types/registries/adapter' {
7+
export default interface AdapterRegistry {
8+
'related-property-path': RelatedPropertyPathAdapter;
9+
} // eslint-disable-line semi
10+
}

app/adapters/search-result.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import ShareAdapter from './share-adapter';
2+
3+
export default class SearchResultAdapter extends ShareAdapter {
4+
}
5+
6+
declare module 'ember-data/types/registries/adapter' {
7+
export default interface AdapterRegistry {
8+
'search-result': SearchResultAdapter;
9+
} // eslint-disable-line semi
10+
}

app/adapters/share-adapter.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import JSONAPIAdapter from '@ember-data/adapter/json-api';
2+
import config from 'ember-get-config';
3+
4+
export default class ShareAdapter extends JSONAPIAdapter {
5+
host = config.OSF.shareBaseUrl.replace(/\/$/, ''); // Remove trailing slash to avoid // in URLs
6+
namespace = 'api/v3';
7+
}

app/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const App = Application.extend({
5656
'osf-router',
5757
],
5858
externalRoutes: {
59+
search: 'search',
5960
'guid-registration': 'guid-registration',
6061
'guid-registration.analytics': 'guid-registration.analytics',
6162
'guid-registration.forks': 'guid-registration.forks',

app/dashboard/template.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@
239239
<OsfLink
240240
data-analytics-name='noteworthy_search'
241241
local-class='btn btn-default'
242-
@href='/search/?q=*'
242+
@route='search'
243243
>
244244
{{t 'dashboard.noteworthy.search_more'}}
245245
</OsfLink>

app/helpers/get-localized-property.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import Helper from '@ember/component/helper';
2+
import { inject as service } from '@ember/service';
3+
import IntlService from 'ember-intl/services/intl';
4+
5+
import { LanguageText } from 'ember-osf-web/models/index-card';
6+
7+
/**
8+
* This helper is used to get a locale-appropriate string for a property from a metadata hash.
9+
* It is used to fetch metadata fields from a index-card's resourceMetadata attribute, but can be used for any
10+
* hash that contains an array of LangaugeText objects.
11+
* If the property is not found, the first value in the array is returned, or if the property is found,
12+
* but there is no locale-appropriate value, the first value in the array is returned.
13+
*
14+
* @example
15+
* ```handlebars
16+
* {{get-localized-property indexCard.resourceMetadata 'title'}}
17+
* ```
18+
* where `indexCard` is an index-card model instance.
19+
* @class get-localized-property
20+
* @param {Object} metadataHash The metadata hash to search for the property
21+
* @param {String} propertyName The name of the property to search for
22+
* @return {String} The locale-appropriate string or the first value in the array or 'Not provided' message
23+
*/
24+
export default class GetLocalizedPropertyHelper extends Helper {
25+
@service intl!: IntlService;
26+
27+
compute([metadataHash, propertyName]: [Record<string, LanguageText[]>, string]): string {
28+
const locale = this.intl.locale;
29+
const valueOptions = metadataHash?.[propertyName];
30+
if (!metadataHash || !valueOptions || valueOptions.length === 0) {
31+
return this.intl.t('helpers.get-localized-property.not-provided');
32+
}
33+
34+
const index = valueOptions.findIndex((valueOption: LanguageText) => valueOption['@language'] === locale);
35+
if (index === -1) {
36+
return valueOptions[0]['@value'];
37+
}
38+
return valueOptions[index]['@value'];
39+
}
40+
}

app/home/-components/hero-banner/component.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import { tagName } from '@ember-decorators/component';
22
import Component from '@ember/component';
33
import { action } from '@ember/object';
44
import { alias } from '@ember/object/computed';
5+
import RouterService from '@ember/routing/router-service';
56
import { inject as service } from '@ember/service';
67
import { camelize } from '@ember/string';
78
import Features from 'ember-feature-flags/services/features';
89
import config from 'ember-get-config';
910

10-
import { serviceLinks } from 'ember-osf-web/const/service-links';
11-
1211
import { layout } from 'ember-osf-web/decorators/component';
1312

1413
import styles from './styles';
@@ -20,13 +19,13 @@ const { featureFlagNames: { ABTesting } } = config;
2019
@tagName('')
2120
export default class HomeHeroBanner extends Component {
2221
@service features!: Features;
22+
@service router!: RouterService;
2323

2424
@alias(`features.${camelize(ABTesting.homePageHeroTextVersionB)}`)
2525
shouldShowVersionB!: boolean;
2626

2727
@action
2828
search(query: string) {
29-
const { search } = serviceLinks;
30-
window.location.href = `${search}?q=${query}&page=1`;
29+
this.router.transitionTo('search', { queryParams: { q: query }});
3130
}
3231
}

app/institutions/dashboard/controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import { alias } from '@ember/object/computed';
12
import Controller from '@ember/controller';
23
import { computed } from '@ember/object';
3-
import { alias } from '@ember/object/computed';
44
import { inject as service } from '@ember/service';
55

66
import { InstitutionsDashboardModel } from 'ember-osf-web/institutions/dashboard/route';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Controller from '@ember/controller';
2+
import { inject as service } from '@ember/service';
3+
import CurrentUser from 'ember-osf-web/services/current-user';
4+
import { tracked } from '@glimmer/tracking';
5+
import { action } from '@ember/object';
6+
import pathJoin from 'ember-osf-web/utils/path-join';
7+
import config from 'ember-get-config';
8+
import { OnSearchParams, ResourceTypeFilterValue } from 'osf-components/components/search-page/component';
9+
10+
export default class InstitutionDiscoverController extends Controller {
11+
@service currentUser!: CurrentUser;
12+
13+
@tracked cardSearchText?: string = '';
14+
@tracked sort?: string = '-relevance';
15+
@tracked resourceType?: ResourceTypeFilterValue | null = null;
16+
17+
queryParams = ['cardSearchText', 'page', 'sort', 'resourceType'];
18+
19+
get defaultQueryOptions() {
20+
return {
21+
publisher: pathJoin(config.OSF.url, 'institutions', this.model.id),
22+
};
23+
}
24+
25+
@action
26+
onSearch(queryOptions: OnSearchParams) {
27+
this.cardSearchText = queryOptions.cardSearchText;
28+
this.sort = queryOptions.sort;
29+
this.resourceType = queryOptions.resourceType;
30+
}
31+
}

app/institutions/discover/route.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Route from '@ember/routing/route';
2+
import RouterService from '@ember/routing/router-service';
3+
import { inject as service } from '@ember/service';
4+
5+
6+
export default class InstitutionDiscoverRoute extends Route {
7+
@service router!: RouterService;
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<SearchPage
2+
@route='search'
3+
@query={{this.q}}
4+
@defaultQueryOptions={{this.defaultQueryOptions}}
5+
@queryParams={{this.queryParams}}
6+
@onSearch={{action this.onSearch}}
7+
@resourceType={{this.resourceType}}
8+
@institution={{this.model}}
9+
@sort={{this.sort}}
10+
@showResourceTypeFilter={{true}}
11+
/>

app/models/index-card-search.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import Model, { AsyncHasMany, attr, hasMany } from '@ember-data/model';
2+
3+
import RelatedPropertyPathModel from './related-property-path';
4+
import SearchResultModel from './search-result';
5+
6+
export interface SearchFilter {
7+
propertyPath: string;
8+
filterValue: string[];
9+
filterType?: string;
10+
}
11+
12+
export const ShareMoreThanTenThousand = 'https://share.osf.io/vocab/2023/trove/ten-thousands-and-more';
13+
14+
export default class IndexCardSearchModel extends Model {
15+
@attr('string') cardSearchText!: string;
16+
@attr('array') cardSearchFilters!: SearchFilter[];
17+
@attr('string') totalResultCount!: number | typeof ShareMoreThanTenThousand;
18+
19+
@hasMany('search-result', { inverse: null })
20+
searchResultPage!: AsyncHasMany<SearchResultModel> & SearchResultModel[];
21+
22+
@hasMany('related-property-path', { inverse: null })
23+
relatedProperties!: RelatedPropertyPathModel[];
24+
25+
get firstPageCursor() {
26+
if (this.searchResultPage.links.first?.href) {
27+
const firstPageLinkUrl = new URL(this.searchResultPage.links.first?.href);
28+
return firstPageLinkUrl.searchParams.get('page[cursor]');
29+
}
30+
return null;
31+
}
32+
33+
get prevPageCursor() {
34+
if (this.searchResultPage.links.prev?.href) {
35+
const prevPageLinkUrl = new URL(this.searchResultPage.links.prev?.href);
36+
return prevPageLinkUrl.searchParams.get('page[cursor]');
37+
}
38+
return null;
39+
}
40+
41+
get nextPageCursor() {
42+
if (this.searchResultPage.links.next?.href) {
43+
const nextPageLinkUrl = new URL(this.searchResultPage.links.next?.href);
44+
return nextPageLinkUrl.searchParams.get('page[cursor]');
45+
}
46+
return null;
47+
}
48+
}
49+
50+
declare module 'ember-data/types/registries/model' {
51+
export default interface ModelRegistry {
52+
'index-card-search': IndexCardSearchModel;
53+
} // eslint-disable-line semi
54+
}

app/models/index-card.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { inject as service } from '@ember/service';
2+
import Model, { AsyncHasMany, attr, hasMany } from '@ember-data/model';
3+
import IntlService from 'ember-intl/services/intl';
4+
5+
import GetLocalizedPropertyHelper from 'ember-osf-web/helpers/get-localized-property';
6+
import { getOwner } from '@ember/application';
7+
8+
export interface LanguageText {
9+
'@language': string;
10+
'@value': string;
11+
}
12+
13+
export default class IndexCardModel extends Model {
14+
@service intl!: IntlService;
15+
16+
@attr('array') resourceType!: string[];
17+
@attr('array') resourceIdentifier!: string[];
18+
// TODO: can we add a type for resourceMetadata?
19+
@attr('object') resourceMetadata!: any;
20+
21+
@hasMany('index-card', { inverse: null })
22+
relatedRecordSet!: AsyncHasMany<IndexCardModel> & IndexCardModel[];
23+
24+
getLocalizedString = new GetLocalizedPropertyHelper(getOwner(this));
25+
26+
get resourceId() {
27+
return this.resourceIdentifier[0];
28+
}
29+
30+
get label() {
31+
const possibleLabelKeys = ['displayLabel', 'name', 'title'];
32+
for (const key of possibleLabelKeys) {
33+
if (this.resourceMetadata[key]) {
34+
const label = this.getLocalizedString.compute([this.resourceMetadata, key]);
35+
// TODO: Get rid of this special casing once we have a decision on how BE should represents OSF provider
36+
if (label === 'OSF') {
37+
return 'OSF Projects';
38+
}
39+
if (label === 'Open Science Framework') {
40+
return 'OSF Preprints';
41+
}
42+
return label;
43+
}
44+
}
45+
return '';
46+
}
47+
}
48+
49+
declare module 'ember-data/types/registries/model' {
50+
export default interface ModelRegistry {
51+
'index-card': IndexCardModel;
52+
} // eslint-disable-line semi
53+
}

app/models/index-value-search.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Model, { AsyncHasMany, attr, hasMany } from '@ember-data/model';
2+
3+
import { SearchFilter } from './index-card-search';
4+
import SearchResultModel from './search-result';
5+
6+
export default class IndexValueSearchModel extends Model {
7+
@attr('string') valueSearchText!: string;
8+
@attr('string') valueSearchPropertyPath!: string;
9+
@attr('string') cardSearchText!: string;
10+
@attr('array') cardSearchFilter!: SearchFilter[];
11+
@attr('number') totalResultCount!: number;
12+
13+
@hasMany('search-result', { inverse: null })
14+
searchResultPage!: AsyncHasMany<SearchResultModel> & SearchResultModel[];
15+
}
16+
17+
declare module 'ember-data/types/registries/model' {
18+
export default interface ModelRegistry {
19+
'index-value-search': IndexValueSearchModel;
20+
} // eslint-disable-line semi
21+
}

0 commit comments

Comments
 (0)