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

New faceted search results component #132

Merged
merged 62 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
219a818
Bare initial code for new faceted search component
ke4 Nov 30, 2022
5c072a5
Add Cypress to this component
ke4 Mar 9, 2023
4f6ae83
Copy cypress test from current faceted search result - WIP
ke4 Mar 10, 2023
3eecce6
Use fix version (1.2.1) from `random-words` module
ke4 Mar 10, 2023
bfe7a11
Add CheckboxFacetGroup component
ke4 Mar 15, 2023
0a2149f
Removed unneeded test folder as we use Cypress for testing
ke4 Mar 15, 2023
6d876f3
Add CheckboxFacetGroup component
ke4 Mar 17, 2023
4e19321
Improve CheckboxFacetGroup component
ke4 Mar 17, 2023
4c5d686
Merge branch 'master' of https://github.com/ebi-gene-expression-group…
ke4 Mar 20, 2023
273ec9a
Add FilterSidebar component
ke4 Mar 24, 2023
83fd0ce
Add MultiselectDropdownFacetGroup component
ke4 Mar 27, 2023
292c32f
Add MultiselectDropdownFacetGroup to FacetGroups cypress tests
ke4 Mar 27, 2023
964eba7
Add MultiselectDropdownFacetGroup to FilterSidebar component
ke4 Mar 28, 2023
3c3be3b
Fix name and description of the facet groups
ke4 Mar 29, 2023
3ea4692
Add FilterList component without fetch loader but processing new resp…
ke4 Mar 29, 2023
68780fe
Add FacetedSearchContainer component - WIP (need to fix onChange prop…
ke4 Apr 3, 2023
1be4b81
Fix some failing test in FacetedSearchContainer cypress test - 1 is s…
ke4 Apr 6, 2023
1de76c8
Fix FacetedSearchContainer - its test is passing now
ke4 Apr 19, 2023
1ffbfea
Fix CheckboxFacetGroup when we select multiply checkbox
ke4 Apr 19, 2023
04a3816
Add missing fixtures
ke4 Apr 19, 2023
77193f7
Fix queryParams prop type definition
ke4 May 12, 2023
dcc4ced
Fix the checkboxes checked state in Checkbox facet group
ke4 May 17, 2023
03c4ff4
Add html demo with real data for the new faceted search result
ke4 May 17, 2023
86117af
Fix species checkbox group behaviour
ke4 May 22, 2023
d33c162
Fix multi select dropdown facet groups behaviour
ke4 May 22, 2023
926b5eb
Fix is marker gene behaviour
ke4 May 22, 2023
a5f932c
Clean the code
ke4 May 22, 2023
3def078
Fix deleting an already selected facet or all of the facets
ke4 May 24, 2023
1834ad0
Fix adding/deleting filter options that contains comma in their names
ke4 May 24, 2023
a9a3535
Fix cypress tests in CheckboxFacetGroup
ke4 May 24, 2023
7a1cd6f
Fix cypress tests in CheckboxFacetGroup and in MultiSelectDropdownFac…
ke4 May 24, 2023
6ccb795
Fix cypress tests in FacetedSearchContainer.cy.js
ke4 May 25, 2023
cffd67c
Add header config to webpack configuration
ke4 May 25, 2023
405c276
Add code coverage for cypress tests
ke4 May 25, 2023
582997a
Update README.md
ke4 May 25, 2023
9f367b0
Optimising REST calls
ke4 Jun 2, 2023
b0dda0f
Reorganising tests for facet groups
ke4 Jun 20, 2023
47bfe2e
Apply code review findings for FacetedSearchContainer test
ke4 Jul 3, 2023
9cd2ce0
Apply code review findings for package.json
ke4 Jul 3, 2023
72e4062
Apply code review findings
ke4 Jul 5, 2023
a697079
Apply code review findings for README
ke4 Jul 5, 2023
e7254f5
Apply code review findings for FilterList and convert it to stateless
ke4 Jul 5, 2023
09429c5
Apply code review findings for CheckboxFacetGroup and convert it to s…
ke4 Jul 6, 2023
181f4c2
Replace id with role
ke4 Jul 6, 2023
9241269
Fix the state modification in FacetedSearchController
ke4 Jul 24, 2023
2ad66f2
Replace ids from the standard vocabulary for roles
ke4 Aug 8, 2023
af6302f
Remove unneeded coveralls dependency
ke4 Aug 8, 2023
49caea9
Update webpack version to ^5.73
ke4 Aug 8, 2023
a66e492
Clean and simplify map usage
ke4 Aug 9, 2023
e3e7f0d
Fix the cleaned code in the previous commit :-)
ke4 Aug 9, 2023
632b9ba
Update Cypress to the latest version
ke4 Aug 10, 2023
1030a87
Simplify more facet groups default properties
ke4 Aug 10, 2023
92c2bba
Generalise facet group initialization
ke4 Aug 10, 2023
9aefbc3
Update the `scripts` section of package.json
ke4 Aug 10, 2023
35ca213
Replaced double quotes to single one in the import section
ke4 Aug 10, 2023
62e76ce
Add back `webpack-cli` as a dev dependency and fix the README
ke4 Aug 11, 2023
421b411
Wait for response of the result list + add general interception for e…
ke4 Aug 21, 2023
3a5c469
Fix onChange property definition
ke4 Aug 21, 2023
b6e01a5
Remove sorting from the demo page as we are not using it
ke4 Sep 14, 2023
bdfba51
Add ReactToolTip to properly show the tooltip on the page
ke4 Sep 14, 2023
9f8ff19
Update cypress version
ke4 Oct 12, 2023
235afd7
Fix quotes
ke4 Oct 12, 2023
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
4 changes: 4 additions & 0 deletions packages/scxa-faceted-search-results-v5/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["@babel/preset-react", "@babel/preset-env"],
"plugins": [ "transform-class-properties", "istanbul" ]
}
6 changes: 6 additions & 0 deletions packages/scxa-faceted-search-results-v5/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
webpack.config.js
serve.config.js
coverage/
__mocks__/
lib/
dist/
3 changes: 3 additions & 0 deletions packages/scxa-faceted-search-results-v5/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "@ebi-gene-expression-group"
}
67 changes: 67 additions & 0 deletions packages/scxa-faceted-search-results-v5/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# IntelliJ IDEA project files
.idea/

# https://github.com/github/gitignore/blob/master/Node.gitignore

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# Build files
dist/
lib/
6 changes: 6 additions & 0 deletions packages/scxa-faceted-search-results-v5/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Ignore everything
*
.*

# But the build files...
!lib/**
127 changes: 127 additions & 0 deletions packages/scxa-faceted-search-results-v5/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Faceted Search Results for Single Cell Expression Atlas

[![Build Status](https://travis-ci.org/ebi-gene-expression-group/scxa-faceted-search-results.svg?branch=master)](https://travis-ci.org/ebi-gene-expression-group/scxa-faceted-search-results) [![Coverage Status](https://coveralls.io/repos/github/ebi-gene-expression-group/scxa-faceted-search-results/badge.svg?branch=master)](https://coveralls.io/github/ebi-gene-expression-group/scxa-faceted-search-results?branch=master)

A lightweight and extensible component to list and filter lists of search results. It receives a URL (as a combination
of `host` and `resource`) to async-fetch the results, and a React component class to visualise each element (e.g. a
card).

The facets are used to render a sidebar on the left as a set of filtering controls.
Filter groups can be displayed either as searchable, multiselect dropdown lists or as checkboxes.

Two more optional props, namely `noResultsMessageFormatter` and `resultsMessageFormatter`, return a string which is
displayed when there are no results, and as a header on top of the search results, respectively. In many occasions it
is useful to display such messages using information returned by the server; that’s the reason why both functions take
the JSON payload as an argument, allowing for some customisation in that regard. For example, if in the case of no
results the server returns a field `reason` you could have something like this:
```
<ReactFacetedSearch
...
noResultsMessageFormatter={(data) => `Your search yielded no results: ${data.reason}`}
```

# Requirements

You should have node version at least 7 or later.
ke4 marked this conversation as resolved.
Show resolved Hide resolved
You can check your current installed version executing this command.
```shell
npm --version
8.5.6
```

## How to execute the unit tests with Cypress

We are using the [Cypress framework](https://docs.cypress.io/guides/overview/why-cypress) for unit testing this package.
You can execute the existing tests in the following way:
1. Type `npx cypress open` in the terminal in the root folder of this package.
2. In the appearing browser window you have to click on `Component Testing`,
select a browser and click on `Start Component Testing in <browser name>`.
3. In the appearing list just click on the test you would like to run and check the results on the screen.

## Try out the component with a given example
Just run [webpack-dev-server](https://github.com/webpack/webpack-dev-server):
```
npx webpack-dev-server --mode=development
```

## How to run test with code coverage in the console

Run tests with:

```shell
npx cypress run --component
```

If you want to omit video recording:

```shell
npx cypress run --component --record false
```

Find coverage reports at: `./coverage/lcov-report/index.html`


## Add code coverage (in a nutshell)

This is an abridged version of https://docs.cypress.io/guides/tooling/code-coverage.

Add the following three packages:
```bash
npm install -D babel-plugin-transform-class-properties babel-plugin-istanbul @cypress/code-coverage
```

Add the two plugins to your `.babelrc` file:
```json
{
"presets": ["@babel/preset-react", "@babel/preset-env"],
"plugins": [ "transform-class-properties", "istanbul" ]
}
```

Add `@cypress/code-coverage` in your `cypress.config.js` file so it looks like this:
```js
const { defineConfig } = require(`cypress`)

module.exports = defineConfig({
component: {
setupNodeEvents(on, config) {
require(`@cypress/code-coverage/task`)(on, config)
// include any other plugin code...

// It's IMPORTANT to return the config object
// with any changed environment variables
return config
},
devServer: {
framework: `react`,
bundler: `webpack`
}
}
})
```

Lastly, add the following import to `cypress/support/component.js`:
```js
import '@cypress/code-coverage/support'
```

# Combining with the EBI Visual Framework
React-Select allows you to style the `div` that encloses the `input` element, but not the `input` element itself.
Therefore it’s convenient to add the snippet below to override the styling set by the
[EBI Visual Framework](https://github.com/ebiwd/EBI-Framework) for `input` elements.
```
.input-clear input, .input-clear input:focus {
height: unset;
box-shadow: none;
margin: 0;
}
```
At the time of writing this applies to the most recent version (v1.3). Wrapping these styles within the appropriate
`div` selector is sufficient. See
https://github.com/ebi-gene-expression-group/scxa-faceted-search-results/blob/master/html/filter-list.html for a
working example.

# A note about building with Webpack
The component uses `async`/`await` to fetch the JSON payload from the server. This requires to prefix the entry with
`@babel/polyfill`. If you are already using an equivalent polyfill or
[Runtime transform](http://babeljs.io/docs/plugins/transform-runtime/) you may skip this.
18 changes: 18 additions & 0 deletions packages/scxa-faceted-search-results-v5/cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const { defineConfig } = require(`cypress`)

module.exports = defineConfig({
component: {
setupNodeEvents(on, config) {
require(`@cypress/code-coverage/task`)(on, config)
// include any other plugin code...

// It's IMPORTANT to return the config object
// with any changed environment variables
return config
},
devServer: {
framework: `react`,
bundler: `webpack`
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react"

import CheckboxFacetGroup from '../../src/facetgroups/CheckboxFacetGroup'

import { getFacets, getPropsForCheckBoxGroupWithTooltip, getPropsWithoutTooltip } from './TestUtils'

describe(`CheckboxFacetGroup`, () => {
it(`displays the expected tooltip if it exists`, () => {
const propsWithTooltip = getPropsForCheckBoxGroupWithTooltip()
const facetTooltip = propsWithTooltip.description
const props = {
...propsWithTooltip,
ke4 marked this conversation as resolved.
Show resolved Hide resolved
facets: getFacets()
}

cy.mount(<CheckboxFacetGroup {...props }/>)
cy.get(`div.padding-bottom-xlarge h4 sup span`)
.should(`have.class`, `icon icon-generic`)
.should(`have.attr`, `data-tip`).then((dataTip) => {
expect(dataTip.toString()
.replace(`<span>`, ``)
.replace(`</span>`, ``)
.replace(`<br>`, ` `))
.to.equal(facetTooltip)
ke4 marked this conversation as resolved.
Show resolved Hide resolved
})
})

it(`doesn't display tooltip if not present`, () => {
const propsWithoutTooltip = {
...getPropsWithoutTooltip(),
facets: getFacets()
}
cy.mount(<CheckboxFacetGroup {...propsWithoutTooltip}/>)
cy.get(`div.padding-bottom-xlarge h4`)
.children()
.should(`have.length`, 0)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React from 'react'

import CheckboxFacetGroup from '../../src/facetgroups/CheckboxFacetGroup'

import vindicators from '../fixtures/vindicatorsResponse.json'
import { getRandomInt } from './TestUtils'
import MultiselectDropdownFacetGroup from "../../src/facetgroups/MultiselectDropdownFacetGroup";

let props = {
name: `Vindicators`,
description: `Show the vindicators`,
facets: [],
queryParams: []
}

describe(`CheckboxFacetGroup`, () => {
ke4 marked this conversation as resolved.
Show resolved Hide resolved

beforeEach(() => {
while (props.facets.length === 0) {
props.facets = vindicators.filter(() => Math.random() > 0.5)
}
props.facets = props.facets.map(e => ({...e, disabled: false}))
})

it(`renders the right number of checkboxes and the facet name`, () => {
cy.mount(<CheckboxFacetGroup {...props} />)

cy.get(`input`).should((inputs) => {
expect(inputs.length).to.equal(props.facets.length)
})
cy.get(`input[type="checkbox"]`).should((checkboxes) => {
expect(checkboxes.length).to.equal(props.facets.length)
})
cy.get(`h4`).invoke(`text`).then((headerText) => {
expect(headerText).to.equal(props.name)
})
cy.get(`input`).should(`not.have.attr`, `disabled`)
cy.get(`label`).should(`not.have.attr`, `color`)
})

it(`displays disabled check boxes greyed out`, () => {
props.facets = props.facets.map(e => ({...e, disabled: true}))
cy.mount(<CheckboxFacetGroup {...props} />)

cy.get(`input`).should(`have.attr`, `disabled`)
cy.get(`label`)
.should(`have.attr`, `style`)
.should(`eq`, `color: lightgrey;`)
})

it(`callback is called when a checkbox is checked/unchecked with the right arguments`, () => {
const randomCheckboxIndex = getRandomInt(0, props.facets.length)
const mockCallbackWrapper = {
onChange: function(facetGroup, checkedFacets) {
}
}
cy.spy(mockCallbackWrapper, `onChange`).as(`onChange`)

cy.mount(<CheckboxFacetGroup {...props} onChange={mockCallbackWrapper.onChange} />)

cy.get(`input[type="checkbox"]`).eq(randomCheckboxIndex).click()
cy.get(`@onChange`)
.should(`have.been.calledOnceWithExactly`,
props.name,
[props.facets[randomCheckboxIndex]],
props.facets
)

cy.get(`input[type="checkbox"]`).eq(randomCheckboxIndex).click()
cy.get(`@onChange`)
.should(`have.been.callCount`, 2)
.should(`have.been.calledWith`,
props.name, [props.facets[randomCheckboxIndex]]
)
.should(`have.been.calledWith`,
props.name, [], props.facets
)
})
})

describe(`MultiselectDropdownFacetGroup`, () => {
ke4 marked this conversation as resolved.
Show resolved Hide resolved

beforeEach(() => {
props.facets = vindicators
})

it(`callback includes user typed facet name in arguments`, () => {
const mockCallbackWrapper = {
onChange: function(facetGroup, checkedFacets) {
}
}
cy.spy(mockCallbackWrapper, `onChange`).as(`onChange`)
const randomIndex = getRandomInt(0, props.facets.length)
const valueToType = vindicators[randomIndex].value

cy.mount(<MultiselectDropdownFacetGroup {...props} onChange={mockCallbackWrapper.onChange} />)
cy.get(`input#facetGroupMultiSelectDropdown`)
.type(`${valueToType}{enter}`, {force: true})

cy.get(`@onChange`)
.should(`have.been.calledOnceWithExactly`,
props.name, [props.facets[randomIndex]], props.facets
)
})
})
Loading