1
0
mirror of https://github.com/oceanprotocol/commons.git synced 2023-03-15 18:03:00 +01:00

live filtering of search results

This commit is contained in:
Matthias Kretschmann 2019-09-13 00:29:09 +02:00
parent bab254e106
commit e9dfd37e3d
Signed by: m
GPG Key ID: 606EEEF3C479A91F
13 changed files with 3011 additions and 164 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,12 @@
import searchMock from '../__fixtures__/search.json'
const oceanMock = {
ocean: {
accounts: {
list: () => ['xxx', 'xxx']
},
aquarius: {
queryMetadata: () => {
return {
results: [],
totalResults: 1,
totalPages: 1
}
}
queryMetadata: () => searchMock
},
assets: {
query: () => {

View File

@ -17,6 +17,8 @@ const AssetTeaser = ({
list?: boolean
minimal?: boolean
}) => {
if (!asset.findServiceByType) return null
const { attributes } = asset.findServiceByType('metadata')
const { main, additionalInformation } = attributes

View File

@ -0,0 +1,39 @@
@import '../../styles/variables';
.item {
position: relative;
&:before {
display: none;
}
}
.option {
padding-top: $spacer / 10;
padding-bottom: $spacer / 10;
padding-right: $spacer / 6;
text-align: left;
color: $brand-grey-light;
font-size: $font-size-small;
transition: .1s ease-out;
}
.active {
composes: item;
.option {
color: $brand-grey-dark;
font-weight: $font-weight-bold;
border-left: .3rem solid $brand-black;
padding-left: $spacer / 4;
}
}
.cancel {
position: absolute;
right: 0;
top: $spacer / 10;
font-size: $font-size-h3;
color: inherit;
line-height: 1;
}

View File

@ -0,0 +1,51 @@
import React from 'react'
import shortid from 'shortid'
import Button from '../../components/atoms/Button'
import styles from './FilterItem.module.scss'
export default function FilterItem({
isActive,
filter,
filterByCategory,
filterByLicense,
option
}: {
isActive: boolean
filter: any
option: string
filterByCategory(category: string): void
filterByLicense(license: string): void
}) {
return (
<li
key={shortid.generate()}
className={isActive ? styles.active : styles.item}
>
<Button
link
className={styles.option}
onClick={() =>
filter.label === 'Category'
? filterByCategory(option)
: filterByLicense(option)
}
>
{option}{' '}
</Button>
{isActive && (
<Button
link
className={styles.cancel}
title="Clear"
onClick={() =>
filter.label === 'Category'
? filterByCategory('')
: filterByLicense('')
}
>
&times;
</Button>
)}
</li>
)
}

View File

@ -3,43 +3,9 @@
.filter {
ul {
padding-left: 0;
li {
position: relative;
&:before {
display: none;
}
}
}
}
.option {
padding-top: $spacer / 10;
padding-bottom: $spacer / 10;
padding-right: $spacer / 6;
text-align: left;
color: $brand-grey-light;
font-size: $font-size-small;
transition: .1s ease-out;
.active & {
color: $brand-grey-dark;
font-weight: $font-weight-bold;
border-left: .3rem solid $brand-black;
padding-left: $spacer / 4;
}
}
.cancel {
position: absolute;
right: 0;
top: $spacer / 10;
font-size: $font-size-h3;
color: inherit;
line-height: 1;
}
.filterTitle {
font-size: $font-size-base;
color: $brand-grey-light;

View File

@ -0,0 +1,22 @@
import React from 'react'
import { render } from '@testing-library/react'
import { User } from '../../context'
import { userMockConnected } from '../../../__mocks__/user-mock'
import Filters from './Filters'
describe('Filters', () => {
it('renders without crashing', () => {
const { debug, container } = render(
<User.Provider value={userMockConnected}>
<Filters
category="Architecture"
license="Public"
results={[]}
filterByCategory={() => null}
filterByLicense={() => null}
/>
</User.Provider>
)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -1,8 +1,9 @@
import React from 'react'
import shortid from 'shortid'
import Button from '../../components/atoms/Button'
import styles from './Filters.module.scss'
import data from '../../data/form-publish.json'
import FilterItem from './FilterItem'
import { DDO } from '@oceanprotocol/squid'
const { steps } = data
@ -19,10 +20,11 @@ const labelLicense =
steps[2].fields.license.label
function getFilterMetadata(results: any[]) {
const filterCategories: string[] = []
const filterLicenses: string[] = []
let filterCategories: string[] = []
let filterLicenses: string[] = []
results.map(asset => {
results.map((asset: DDO) => {
if (!asset.findServiceByType) return null
const { metadata } = asset.findServiceByType('Metadata')
const { categories, license } = metadata.base
categories && filterCategories.push(categories[0])
@ -30,6 +32,10 @@ function getFilterMetadata(results: any[]) {
return null
})
// remove duplicates
filterCategories = Array.from(new Set(filterCategories))
filterLicenses = Array.from(new Set(filterLicenses))
return { filterCategories, filterLicenses }
}
@ -37,14 +43,14 @@ export default function Filters({
category,
license,
results,
setCategory,
setLicense
filterByCategory,
filterByLicense
}: {
category: string
license: string
results: any[]
setCategory(category: string): void
setLicense(license: string): void
filterByCategory(category: string): void
filterByLicense(license: string): void
}) {
const { filterCategories, filterLicenses } = getFilterMetadata(results)
@ -70,41 +76,13 @@ export default function Filters({
license === option
return (
<li
key={shortid.generate()}
className={
isActive
? styles.active
: undefined
}
>
<Button
link
className={styles.option}
onClick={() =>
filter.label === 'Category'
? setCategory(option)
: setLicense(option)
}
>
{option}{' '}
</Button>
{isActive && (
<Button
link
className={styles.cancel}
title="Clear"
onClick={() =>
filter.label ===
'Category'
? setCategory('')
: setLicense('')
}
>
&times;
</Button>
)}
</li>
<FilterItem
isActive={isActive}
filter={filter}
filterByCategory={filterByCategory}
filterByLicense={filterByLicense}
option={option}
/>
)
})}
</ul>

View File

@ -0,0 +1,32 @@
@import '../../styles/variables';
.resultsTitle {
color: $brand-grey-light;
font-size: $font-size-h3;
margin-top: 0;
margin-bottom: $spacer;
span {
color: $brand-grey-dark;
}
}
.results {
display: grid;
grid-template-columns: 1fr;
grid-gap: $spacer;
@media (min-width: $break-point--medium) {
grid-template-columns: repeat(2, 1fr);
}
@media (min-width: $break-point--large) {
grid-template-columns: repeat(3, 1fr);
}
}
.empty {
text-align: center;
margin-top: $spacer * 4;
color: $brand-grey-light;
}

View File

@ -0,0 +1,46 @@
import React from 'react'
import { Link } from 'react-router-dom'
import shortid from 'shortid'
import AssetTeaser from '../../components/molecules/AssetTeaser'
import Pagination from '../../components/molecules/Pagination'
import styles from './Results.module.scss'
export default function Results({
title,
results,
totalResults,
totalPages,
currentPage,
handlePageClick
}: {
title: string
results: any[]
totalResults: number
totalPages: number
currentPage: number
handlePageClick(data: { selected: number }): Promise<any>
}) {
return results && results.length ? (
<>
<h2 className={styles.resultsTitle}>
{totalResults} results for <span>{title}</span>
</h2>
<div className={styles.results}>
{results.map((asset: any) => (
<AssetTeaser key={shortid.generate()} asset={asset} />
))}
</div>
<Pagination
totalPages={totalPages}
currentPage={currentPage}
handlePageClick={handlePageClick}
/>
</>
) : (
<div className={styles.empty}>
<p>No Data Sets Found.</p>
<Link to="/publish">+ Publish A Data Set</Link>
</div>
)
}

View File

@ -9,16 +9,16 @@ export default function Sidebar({
category,
license,
results,
setCategory,
setLicense
filterByCategory,
filterByLicense
}: {
search: string
inputChange: any
category: string
license: string
results: any[]
setCategory(category: string): void
setLicense(license: string): void
filterByCategory(category: string): void
filterByLicense(license: string): void
}) {
return (
<aside className={styles.sidebar}>
@ -40,8 +40,8 @@ export default function Sidebar({
category={category}
license={license}
results={results}
setCategory={setCategory}
setLicense={setLicense}
filterByCategory={filterByCategory}
filterByLicense={filterByLicense}
/>
</aside>
)

View File

@ -9,34 +9,3 @@
grid-template-columns: 1fr 3fr;
}
}
.resultsTitle {
color: $brand-grey-light;
font-size: $font-size-h3;
margin-top: 0;
margin-bottom: $spacer;
span {
color: $brand-grey-dark;
}
}
.results {
display: grid;
grid-template-columns: 1fr;
grid-gap: $spacer;
@media (min-width: $break-point--medium) {
grid-template-columns: repeat(2, 1fr);
}
@media (min-width: $break-point--large) {
grid-template-columns: repeat(3, 1fr);
}
}
.empty {
text-align: center;
margin-top: $spacer * 4;
color: $brand-grey-light;
}

View File

@ -1,18 +1,15 @@
import React, { PureComponent, ChangeEvent } from 'react'
import { Link } from 'react-router-dom'
import queryString from 'query-string'
import { History, Location } from 'history'
import shortid from 'shortid'
import { Logger } from '@oceanprotocol/squid'
import { Logger, DDO } from '@oceanprotocol/squid'
import Spinner from '../../components/atoms/Spinner'
import Route from '../../components/templates/Route'
import { User } from '../../context'
import AssetTeaser from '../../components/molecules/AssetTeaser'
import Pagination from '../../components/molecules/Pagination'
import styles from './index.module.scss'
import Content from '../../components/atoms/Content'
import withTracker from '../../hoc/withTracker'
import Sidebar from './Sidebar'
import Results from './Results'
import styles from './index.module.scss'
interface SearchProps {
location: Location
@ -21,6 +18,7 @@ interface SearchProps {
interface SearchState {
results: any[]
resultsFiltered: any[]
totalResults: number
offset: number
totalPages: number
@ -38,6 +36,7 @@ class Search extends PureComponent<SearchProps, SearchState> {
public state = {
results: [],
resultsFiltered: [],
totalResults: 0,
offset: 25,
totalPages: 1,
@ -112,6 +111,7 @@ class Search extends PureComponent<SearchProps, SearchState> {
const search = await ocean.assets.query(searchQuery)
this.setState({
results: search.results,
resultsFiltered: search.results,
totalResults: search.totalResults,
totalPages: search.totalPages,
isLoading: false
@ -161,32 +161,37 @@ class Search extends PureComponent<SearchProps, SearchState> {
this.setState({ category, isLoading: true }, () => this.searchAssets())
}
public filterByCategory = (category: string) => {
const resultsFiltered: any[] = this.state.results.filter(
(asset: DDO) => {
const { metadata } = asset.findServiceByType('Metadata')
const { categories } = metadata.base
if (!categories) return true
return category === '' ? true : categories.includes(category)
}
)
this.setState({ resultsFiltered, category })
}
public filterByLicense = (name: string) => {
const resultsFiltered: any[] = this.state.results.filter(
(asset: any) => {
const { metadata } = asset.findServiceByType('Metadata')
const { license } = metadata.base
return name === '' ? true : license === name
}
)
this.setState({ resultsFiltered, license: name })
}
public setLicense = (license: string) => {
this.setState({ license, isLoading: true }, () => this.searchAssets())
}
public renderResults = () =>
this.state.results && this.state.results.length ? (
<>
<div className={styles.results}>
{this.state.results.map((asset: any) => (
<AssetTeaser key={shortid.generate()} asset={asset} />
))}
</div>
<Pagination
totalPages={this.state.totalPages}
currentPage={this.state.currentPage}
handlePageClick={this.handlePageClick}
/>
</>
) : (
<div className={styles.empty}>
<p>No Data Sets Found.</p>
<Link to="/publish">+ Publish A Data Set</Link>
</div>
)
public render() {
const {
isLoading,
@ -194,7 +199,10 @@ class Search extends PureComponent<SearchProps, SearchState> {
totalResults,
search,
category,
license
license,
resultsFiltered,
totalPages,
currentPage
} = this.state
return (
@ -207,26 +215,24 @@ class Search extends PureComponent<SearchProps, SearchState> {
category={category}
results={results}
license={license}
setCategory={this.setCategory}
setLicense={this.setLicense}
filterByCategory={this.filterByCategory}
filterByLicense={this.filterByLicense}
/>
<div>
{isLoading ? (
<Spinner message="Searching..." />
) : (
<>
<h2 className={styles.resultsTitle}>
{totalResults} results for{' '}
<span>
{decodeURIComponent(
search || category
)}
</span>
</h2>
{this.renderResults()}
</>
<Results
title={decodeURIComponent(
search || category
)}
results={resultsFiltered}
totalResults={totalResults}
totalPages={totalPages}
currentPage={currentPage}
handlePageClick={this.handlePageClick}
/>
)}
</div>
</div>