Skip to content

Commit

Permalink
Merge pull request Sage-Bionetworks#1234 from jay-hodgson/PORTALS-3252
Browse files Browse the repository at this point in the history
PORTALS-3252
  • Loading branch information
jay-hodgson authored Sep 24, 2024
2 parents 0754c00 + 39fc3f4 commit 9eabcbd
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SynapseConfig } from '@sage-bionetworks/synapse-portal-framework/types/portal-config'
import { SynapseConstants } from 'synapse-react-client'
import { publicationsSql } from '../resources'
import { Direction } from '@sage-bionetworks/synapse-types'

const rgbIndex = 5

Expand Down Expand Up @@ -33,6 +34,7 @@ const publications: SynapseConfig = {
sql: publicationsSql,
name: 'Publications',
shouldDeepLink: true,
facetValueSortConfigs: [{ columnName: 'year', direction: Direction.DESC }],
facetsToPlot: ['Program', 'year', 'grant', 'journal'],
cardConfiguration: publicationCardProps,
columnAliases,
Expand Down
2 changes: 2 additions & 0 deletions apps/portals/bsmn/src/config/synapseConfigs/publications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SynapseConfig } from '@sage-bionetworks/synapse-portal-framework/types/
import { SynapseConstants } from 'synapse-react-client'
import type { CardConfiguration } from 'synapse-react-client'
import { publicationsSql } from '../resources'
import { Direction } from '@sage-bionetworks/synapse-types'

const rgbIndex = 5

Expand All @@ -25,6 +26,7 @@ const publications: SynapseConfig = {
name: 'Publications',
cardConfiguration: publicationsCardConfiguration,
sql: publicationsSql,
facetValueSortConfigs: [{ columnName: 'year', direction: Direction.DESC }],
facetsToPlot: ['grantNumber', 'year', 'journal', 'projectTitle'],
searchConfiguration: {
searchable: ['title', 'authors', 'year', 'journal', 'grantNumber'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { GenericCardSchema } from 'synapse-react-client'
import type { CardConfiguration } from 'synapse-react-client'
import columnAliases from '../columnAliases'
import { publicationSql } from '../resources'
import { Direction } from '@sage-bionetworks/synapse-types'

const rgbIndex = 1

Expand Down Expand Up @@ -68,6 +69,9 @@ export const publications: SynapseConfig = {
shouldDeepLink: true,
name: 'Publications',
columnAliases,
facetValueSortConfigs: [
{ columnName: 'publicationYear', direction: Direction.DESC },
],
searchConfiguration: {
searchable: [
'publicationTitle',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SynapseConfig } from '@sage-bionetworks/synapse-portal-framework/types/portal-config'
import { SynapseConstants } from 'synapse-react-client'
import { defaultSearchConfiguration, publicationsSql } from '../resources'
import { Direction } from '@sage-bionetworks/synapse-types'

const rgbIndex = 5

Expand All @@ -23,6 +24,7 @@ const publications: SynapseConfig = {
name: 'Publications',
shouldDeepLink: true,
facetsToPlot: ['Program', 'Year', 'Grant', 'Journal'],
facetValueSortConfigs: [{ columnName: 'Year', direction: Direction.DESC }],
cardConfiguration: publicationCardProps,
searchConfiguration: defaultSearchConfiguration,
},
Expand Down
2 changes: 2 additions & 0 deletions apps/portals/nf/src/config/synapseConfigs/publications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SynapseConfig } from '@sage-bionetworks/synapse-portal-framework/types/
import { columnAliases } from './commonProps'

import { publicationsSql } from '../resources'
import { Direction } from '@sage-bionetworks/synapse-types'

export const newPublicationsSql = `${publicationsSql} order by ROW_ID desc limit 3`
const type = SynapseConstants.GENERIC_CARD
Expand Down Expand Up @@ -50,6 +51,7 @@ const publications: SynapseConfig = {
name: 'Publications',
cardConfiguration: publicationsCardConfiguration,
columnAliases,
facetValueSortConfigs: [{ columnName: 'year', direction: Direction.DESC }],
searchConfiguration: {
searchable: [
'title',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ColumnMultiValueFunction,
ColumnSingleValueFilterOperator,
ColumnSingleValueQueryFilter,
Direction,
Query,
} from '@sage-bionetworks/synapse-types'
import QueryWrapperPlotNav, {
Expand Down Expand Up @@ -76,6 +77,9 @@ export const Cards: Story = {
defaultShowPlots: false,
defaultShowSearchBox: true,
shouldDeepLink: true,
facetValueSortConfigs: [
{ columnName: 'usageRequirements', direction: Direction.DESC },
],
cardConfiguration: {
type: GENERIC_CARD,
titleLinkConfig: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ type QueryWrapperPlotNavOwnProps = {
>
facetsToPlot?: string[]
availableFacets?: FacetFilterControlsProps['availableFacets']
/**
* Controls the sort order (ascending or descending) of facet values for a particular column.
* Note: This parameter does not currently apply to the pie/bar chart visualizations, but it probably should.
*/
facetValueSortConfigs?: FacetFilterControlsProps['facetValueSortConfigs']
customPlots?: QueryWrapperSynapsePlotProps[]
defaultColumn?: string
defaultShowSearchBox?: boolean
Expand Down Expand Up @@ -122,6 +127,7 @@ type QueryWrapperPlotNavContentsProps = Pick<
| 'cardConfiguration'
| 'facetsToPlot'
| 'availableFacets'
| 'facetValueSortConfigs'
| 'hideDownload'
| 'hideQueryCount'
| 'hideSqlEditorControl'
Expand All @@ -146,6 +152,7 @@ function QueryWrapperPlotNavContents(props: QueryWrapperPlotNavContentsProps) {
cardConfiguration,
facetsToPlot,
availableFacets,
facetValueSortConfigs,
hideDownload,
hideQueryCount,
hideSqlEditorControl,
Expand Down Expand Up @@ -231,7 +238,10 @@ function QueryWrapperPlotNavContents(props: QueryWrapperPlotNavContentsProps) {
</SynapseErrorBoundary>
{isFaceted && (
<>
<FacetFilterControls availableFacets={availableFacets} />
<FacetFilterControls
availableFacets={availableFacets}
facetValueSortConfigs={facetValueSortConfigs}
/>
</>
)}
<TotalQueryResults
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@ describe('SharePageLinkButton', () => {
afterAll(() => server.close())
beforeEach(() => {
jest.clearAllMocks()
// Replace clipboard.writeText with a mock
})

it('Copies short.io response to clipboard', async () => {
Object.assign(navigator, {
clipboard: {
writeText: jest.fn().mockImplementation(() => Promise.resolve()),
},
})
})

it('Copies short.io response to clipboard', async () => {
renderComponent({ shortIoPublicApiKey: 'abc' })
expect(screen.queryByRole('alert')).not.toBeInTheDocument()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { EnumFacetFilter, EnumFacetFilterProps } from './EnumFacetFilter'
import {
ColumnModel,
ColumnTypeEnum,
Direction,
FacetColumnResultValueCount,
FacetColumnResultValues,
QueryBundleRequest,
Expand Down Expand Up @@ -65,6 +66,16 @@ const entityFacetValues: FacetColumnResultValueCount[] = [
{ value: mockTableEntity.id.replace('syn', ''), count: 1, isSelected: false },
]

const integerFacetValues: FacetColumnResultValueCount[] = [
{ value: '20030', count: 2, isSelected: false },
{ value: '2010', count: 1, isSelected: true },
{
value: SynapseConstants.VALUE_NOT_SET,
count: 1,
isSelected: false,
},
]

const columnModel: ColumnModel = {
columnType: ColumnTypeEnum.STRING,
facetType: 'enumeration',
Expand All @@ -79,6 +90,19 @@ const facet: FacetColumnResultValues = {
concreteType: 'org.sagebionetworks.repo.model.table.FacetColumnResultValues',
}

const integerColumnModel: ColumnModel = {
columnType: ColumnTypeEnum.INTEGER,
facetType: 'enumeration',
id: '86424',
name: 'Year',
}
const integerFacet: FacetColumnResultValues = {
columnName: 'Year',
facetType: 'enumeration',
facetValues: integerFacetValues,
concreteType: 'org.sagebionetworks.repo.model.table.FacetColumnResultValues',
}

function createTestProps(
overrides?: Partial<EnumFacetFilterProps>,
): EnumFacetFilterProps {
Expand Down Expand Up @@ -193,6 +217,75 @@ describe('EnumFacetFilter', () => {
expect(counts[2]).toHaveTextContent(`${stringFacetValues[2].count}`)
})

it('should set labels correctly for INTEGER type', async () => {
registerTableQueryResult(nextQueryRequest.query, {
...mockQueryResponseData,
columnModels: [integerColumnModel],
})
const { container } = await init({ facet: integerFacet })

const checkboxes = await screen.findAllByRole<HTMLInputElement>(
'checkbox',
)
const counts = container.querySelectorAll<HTMLDivElement>(
'.EnumFacetFilter__count',
)

expect(checkboxes).toHaveLength(4)
expect(counts).toHaveLength(3)

expect(checkboxes[0]).toHaveAccessibleName('All')

// Note: Facet values are resorted to numberical order! [1] should appear before [0]
expect(checkboxes[1]).toHaveAccessibleName(
`${integerFacetValues[1].value}`,
)
expect(counts[0]).toHaveTextContent(`${integerFacetValues[1].count}`)

expect(checkboxes[2]).toHaveAccessibleName(
`${integerFacetValues[0].value}`,
)
expect(counts[1]).toHaveTextContent(`${integerFacetValues[0].count}`)

expect(checkboxes[3]).toHaveAccessibleName(`Not Assigned`)
expect(counts[2]).toHaveTextContent(`${integerFacetValues[2].count}`)
})

it('should reverse sort order if configured', async () => {
const { container } = await init({
sortConfig: {
columnName: 'MAKE',
direction: Direction.DESC,
},
})

const checkboxes = await screen.findAllByRole<HTMLInputElement>(
'checkbox',
)
const counts = container.querySelectorAll<HTMLDivElement>(
'.EnumFacetFilter__count',
)

expect(checkboxes).toHaveLength(4)
expect(counts).toHaveLength(3)

expect(checkboxes[0]).toHaveAccessibleName('All')

// PORTALS-3252 note: Facet values are reverse sorted! [0] will appear before [1] again
expect(checkboxes[1]).toHaveAccessibleName(
`${stringFacetValues[0].value}`,
)
expect(counts[0]).toHaveTextContent(`${stringFacetValues[0].count}`)

expect(checkboxes[2]).toHaveAccessibleName(
`${stringFacetValues[1].value}`,
)
expect(counts[1]).toHaveTextContent(`${stringFacetValues[1].count}`)

expect(checkboxes[3]).toHaveAccessibleName(`Not Assigned`)
expect(counts[2]).toHaveTextContent(`${stringFacetValues[2].count}`)
})

it('should set labels correctly for ENTITYID type', async () => {
const entityColumnModel: ColumnModel = {
...columnModel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { Suspense, useMemo } from 'react'
import useGetInfoFromIds from '../../../../utils/hooks/useGetInfoFromIds'
import {
ColumnTypeEnum,
Direction,
EntityHeader,
Evaluation,
FacetColumnRequest,
Expand Down Expand Up @@ -29,6 +30,12 @@ export type EnumFacetFilterProps = {
containerAs?: 'Collapsible' | 'Dropdown'
dropdownType?: 'Icon' | 'SelectBox'
hideCollapsible?: boolean
sortConfig?: FacetValueSortConfig
}

export type FacetValueSortConfig = {
columnName: string
direction: Direction
}

function _EnumFacetFilter(props: EnumFacetFilterProps) {
Expand All @@ -37,6 +44,7 @@ function _EnumFacetFilter(props: EnumFacetFilterProps) {
containerAs = 'Collapsible',
dropdownType = 'Icon',
hideCollapsible = false,
sortConfig,
} = props
const {
nextQueryRequest,
Expand Down Expand Up @@ -82,6 +90,17 @@ function _EnumFacetFilter(props: EnumFacetFilterProps) {
? getCorrespondingColumnForFacet(facet, queryMetadata.columnModels)
: undefined

const isNumberColumnType = useMemo(() => {
switch (columnModel?.columnType) {
case ColumnTypeEnum.DOUBLE:
case ColumnTypeEnum.DATE:
case ColumnTypeEnum.INTEGER:
return true
default:
return false
}
}, [columnModel])

const userIds =
columnModel?.columnType === ColumnTypeEnum.USERID ||
columnModel?.columnType === ColumnTypeEnum.USERID_LIST
Expand Down Expand Up @@ -137,8 +156,19 @@ function _EnumFacetFilter(props: EnumFacetFilterProps) {
)
const valueNotSetFacetArray = partitions[0]
const restOfFacetValuesArray = partitions[1]
let sortedValues: RenderedFacetValue[]
if (isNumberColumnType) {
sortedValues = sortBy(restOfFacetValuesArray, fv => Number(fv.value))
} else {
sortedValues = sortBy(restOfFacetValuesArray, fv =>
fv.displayText.toLowerCase(),
)
}

//PORTALS-3252: provide way to sort in descending order on the client-side
const sortDescending = sortConfig && sortConfig.direction == Direction.DESC
return [
...sortBy(restOfFacetValuesArray, fv => fv.displayText.toLowerCase()),
...(sortDescending ? sortedValues.reverse() : sortedValues),
...valueNotSetFacetArray,
]
}, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {
QueryBundleRequest,
} from '@sage-bionetworks/synapse-types'
import { useQueryContext } from '../../QueryContext'
import { EnumFacetFilter } from './EnumFacetFilter/EnumFacetFilter'
import {
EnumFacetFilter,
FacetValueSortConfig,
} from './EnumFacetFilter/EnumFacetFilter'
import { FacetChip } from './FacetChip'
import { RangeFacetFilter } from './RangeFacetFilter'
import { groupBy, noop, sortBy } from 'lodash-es'
Expand All @@ -29,6 +32,7 @@ export type FacetFilterControlsProps = {
/* The set of faceted column names that should be shown in the Facet controls. If undefined, all faceted columns with
at least one non-null value will be shown. */
availableFacets?: string[]
facetValueSortConfigs?: FacetValueSortConfig[]
}

const convertFacetToFacetColumnValuesRequest = (
Expand Down Expand Up @@ -98,7 +102,7 @@ export function applyChangesToValuesColumn(
}

function FacetFilterControls(props: FacetFilterControlsProps) {
const { availableFacets } = props
const { availableFacets, facetValueSortConfigs } = props
const {
getCurrentQueryRequest,
combineRangeFacetConfig,
Expand Down Expand Up @@ -226,10 +230,17 @@ function FacetFilterControls(props: FacetFilterControlsProps) {
)}
{shownTopLevelFacets.map(facet => {
const columnModel = getCorrespondingColumnForFacet(facet, columnModels!)
const sortConfig = facetValueSortConfigs?.find(
config => config.columnName == facet.columnName,
)
return (
<div className="FacetFilterControls__facet" key={facet.columnName}>
{facet.facetType === 'enumeration' && columnModel && (
<EnumFacetFilter containerAs="Collapsible" facet={facet} />
<EnumFacetFilter
containerAs="Collapsible"
facet={facet}
sortConfig={sortConfig}
/>
)}
{facet.facetType === 'range' && columnModel && (
<RangeFacetFilter facetResult={facet} />
Expand Down

0 comments on commit 9eabcbd

Please sign in to comment.