Commit d2627a64 authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Merge branch '734-statistics-grid-update' into 'v1.0.2'

Resolve "Statistics grid update"

See merge request !557
parents 9fc526b7 45a1b16b
Pipeline #121571 passed with stages
in 38 minutes and 49 seconds
......@@ -53,7 +53,7 @@ export const labelArchive = 'Archive'
/**
* Used to gather a list of fixed filter options from the metainfo.
* @param {*} quantity Metainfo name
* @param {string} quantity Metainfo name
* @returns Dictionary containing the available options and their labels.
*/
function getEnumOptions(quantity) {
......@@ -281,7 +281,7 @@ const termQuantityBool = {
}
}
const termQuantityNonExclusive = {agg: 'terms', aggDefaultSize: 5, stats: listStatConfig, exclusive: false}
const noAggQuantity = {stats: listStatConfig}
const noAggQuantity = {}
const nestedQuantity = {}
const noQueryQuantity = {guiOnly: true, multiple: false}
const rangeQuantity = {agg: 'min_max', multiple: false}
......@@ -316,12 +316,12 @@ registerFilter('external_db', labelAuthor, {...termQuantity, label: 'External Da
registerFilter('authors.name', labelAuthor, {...termQuantityNonExclusive, label: 'Author Name'})
registerFilter('upload_create_time', labelAuthor, rangeQuantity)
registerFilter('datasets.dataset_name', labelDataset, {...termQuantity, label: 'Dataset Name', aggDefaultSize: 10})
registerFilter('datasets.doi', labelDataset, {...noAggQuantity, label: 'Dataset DOI'})
registerFilter('entry_id', labelIDs, noAggQuantity)
registerFilter('upload_id', labelIDs, noAggQuantity)
registerFilter('datasets.doi', labelDataset, {...termQuantity, label: 'Dataset DOI'})
registerFilter('entry_id', labelIDs, termQuantity)
registerFilter('upload_id', labelIDs, termQuantity)
registerFilter('quantities', labelArchive, {...noAggQuantity, label: 'Metainfo definition', queryMode: 'all'})
registerFilter('results.material.material_id', labelIDs, noAggQuantity)
registerFilter('datasets.dataset_id', labelIDs, noAggQuantity)
registerFilter('results.material.material_id', labelIDs, termQuantity)
registerFilter('datasets.dataset_id', labelIDs, termQuantity)
registerFilter(
'results.properties.spectroscopy.eels',
labelSpectroscopy,
......@@ -410,7 +410,7 @@ registerFilter(
registerFilter(
'results.properties.available_properties',
labelProperties,
{noAggQuantity, multiple: true, exclusive: false, queryMode: 'all'}
{termQuantity, multiple: true, exclusive: false, queryMode: 'all'}
)
registerFilter(
'results.properties.mechanical.energy_volume_curve',
......
......@@ -188,6 +188,7 @@ export const SearchContext = React.memo(({
useSetStatistic,
useStatisticState,
useStatisticsValue,
useStatisticsState,
useResults,
useApiData,
useAgg,
......@@ -290,11 +291,7 @@ export const SearchContext = React.memo(({
const statisticsState = selector({
key: `statisticsState_${indexContext}`,
set: ({set}, stats) => {
if (stats) {
for (let [key, value] of Object.entries(stats)) {
set(statisticFamily(key), value)
}
}
stats && Object.entries(stats).forEach(([key, value]) => set(statisticFamily(key), value))
},
get: ({get}) => {
const stats = {}
......@@ -324,7 +321,7 @@ export const SearchContext = React.memo(({
* not interested in reading it.
*
* @param {string} name Name of the quantity to set.
* @returns function for setting the value
* @returns Function for setting the value
*/
function useSetStatistic(name) {
return useSetRecoilState(statisticFamily(name))
......@@ -336,7 +333,7 @@ export const SearchContext = React.memo(({
* filter value.
*
* @param {string} name Name of the filter.
* @returns Array containing the value and setter function for it.
* @returns Array containing the value and a function for setting it.
*/
function useStatisticState(name) {
return useRecoilState(statisticFamily(name))
......@@ -351,6 +348,16 @@ export const SearchContext = React.memo(({
return useRecoilValue(statisticsState)
}
/**
* This hook will expose a function for reading and writing the object
* containing all the current statistics.
*
* @returns Array containing the value and a function for setting it.
*/
function useStatisticsState() {
return useRecoilState(statisticsState)
}
const resultsState = atom({
key: `results_${indexContext}`,
default: {
......@@ -678,6 +685,7 @@ export const SearchContext = React.memo(({
useSetStatistic,
useStatisticState,
useStatisticsValue,
useStatisticsState,
useResults,
useApiData,
useAgg,
......@@ -947,6 +955,7 @@ export const SearchContext = React.memo(({
useSetStatistic: useSetStatistic,
useStatisticState: useStatisticState,
useStatisticsValue: useStatisticsValue,
useStatisticsState: useStatisticsState,
useUpdateQueryString: useUpdateQueryString,
useResults: useResults,
useApiData: useApiData,
......@@ -969,6 +978,7 @@ export const SearchContext = React.memo(({
useSetStatistic,
useStatisticState,
useStatisticsValue,
useStatisticsState,
useUpdateQueryString,
useRefresh,
useResults,
......@@ -1016,13 +1026,8 @@ function qsToSearch(queryString) {
let statistics = {}
const stats = queryObj.statistics
if (stats) {
if (isArray(stats)) {
for (const stat of stats) {
statistics[stat] = true
}
} else {
statistics[stats] = true
}
const statsArray = isArray(stats) ? stats : [stats]
statsArray.forEach((name, i) => { statistics[name] = {index: i} })
delete queryObj.statistics
}
......@@ -1093,9 +1098,13 @@ export function searchToQsData(search) {
}
}
}
// The shown statistics are serialized here: the order is preserved
// The shown statistics are serialized here: currently we only store a simple
// ordered list in the query string. If more properties are needed in the
// future, the whole object should be stored.
if (!isEmpty(statistics)) {
queryStringQuery.statistics = Object.keys(statistics)
queryStringQuery.statistics = Object.entries(statistics)
.sort((a, b) => a[1].index - b[1].index)
.map(([key, value]) => key)
}
return queryStringQuery
......
......@@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useEffect, useRef, useMemo } from 'react'
import React, { useEffect, useRef, useMemo, useCallback } from 'react'
import { Paper } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import PropTypes from 'prop-types'
......@@ -74,9 +74,9 @@ const StatisticsGrid = React.memo(({
classes
}) => {
const styles = useStyles(classes)
const { useStatisticsValue, filterData } = useSearchContext()
const { useStatisticsState, filterData } = useSearchContext()
const { width, ref } = useResizeDetector()
const statistics = useStatisticsValue()
const [statistics, setStatistics] = useStatisticsState()
const gridRef = useRef()
let size
if (width > 1450) {
......@@ -89,6 +89,16 @@ const StatisticsGrid = React.memo(({
size = 'sm'
}
// Store the order when items have been rearranged.
const handleReorder = useCallback((item) => {
const grid = item.getGrid()
const items = grid.getItems()
const keys = items.map((item) => item.getKey())
const stats = {}
keys.forEach((name, i) => { stats[name] = {index: i} })
setStatistics(stats)
}, [setStatistics])
// When the container size changes, the layout needs to be updated.
useEffect(() => {
if (gridRef.current) {
......@@ -98,7 +108,8 @@ const StatisticsGrid = React.memo(({
}, [width])
// Memoize the grid so that it gets rendered only when the contents or size
// changes.
// changes. Re-ordering the items still causes a new re-render: but for now
// this seems to not be a problem.
const content = useMemo(() => {
return (!isEmpty(statistics))
? <div className={styles.container}>
......@@ -106,32 +117,35 @@ const StatisticsGrid = React.memo(({
ref={gridRef}
dragEnabled
layoutOnResize={false}
onDragEnd={handleReorder}
dragHandle=".dragHandle"
showDuration={0}
hideDuration={0}
>
{Object.keys(statistics).map((filter) => {
const config = filterData[filter].stats
const layout = config.layout
const muuriOuterItem = getResponsiveStyle({
columns: widthMapping[layout.width][size] / 12,
ratio: layout.ratio
})
return <div key={filter} style={muuriOuterItem}>
<Paper className={styles.muuriInnerItem}>
<config.component
quantity={filter}
visible
draggable
aggId="statistics"
/>
</Paper>
</div>
})}
{Object.entries(statistics)
.sort((a, b) => a[1].index - b[1].index)
.map(([filter, value]) => {
const config = filterData[filter].stats
const layout = config.layout
const muuriOuterItem = getResponsiveStyle({
columns: widthMapping[layout.width][size] / 12,
ratio: layout.ratio
})
return <div key={filter} style={muuriOuterItem}>
<Paper className={styles.muuriInnerItem}>
<config.component
quantity={filter}
visible
draggable
aggId="statistics"
/>
</Paper>
</div>
})}
</MuuriComponent>
</div>
: null
}, [statistics, filterData, styles, size])
}, [statistics, filterData, styles, handleReorder, size])
return <div ref={ref} className={clsx(className, styles.root)}>
{content}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment