Skip to content

Commit

Permalink
Merge pull request #1442 from nickgros/SWC-6910
Browse files Browse the repository at this point in the history
  • Loading branch information
nickgros authored Dec 9, 2024
2 parents 1fcb672 + 5e87476 commit 9cb7ad9
Show file tree
Hide file tree
Showing 23 changed files with 559 additions and 634 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ export function ChallengeDataDownload({
)

const onAddClick = useCallback(() => {
const entities = selectedEntities.toArray().map(entity => {
const entities = Array.from(selectedEntities.values()).map(reference => {
return {
fileEntityId: entity[0],
versionNumber: entity[1],
fileEntityId: reference.targetId,
versionNumber: reference.targetVersionNumber,
}
})
addBatchToDownloadList(entities)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
isContainerType,
isVersionableEntityType,
} from '../../utils/functions/EntityTypeUtils'
import { NO_VERSION_NUMBER } from '../EntityFinder/EntityFinder'
import { VersionSelectionType } from '../EntityFinder/VersionSelectionType'
import {
CellRendererProps,
Expand Down Expand Up @@ -130,14 +129,7 @@ export const ChallengeDataTable: React.FunctionComponent<DetailsViewProps> = ({
return selected.has(e.id)
})
.map(e => {
const selectedVersion = selected.get(e.id)
return {
targetId: e.id,
targetVersionNumber:
selectedVersion === NO_VERSION_NUMBER
? undefined
: selectedVersion,
}
return selected.get(e.id)!
}),
)
} else {
Expand Down Expand Up @@ -231,13 +223,12 @@ export const ChallengeDataTable: React.FunctionComponent<DetailsViewProps> = ({
// only include entities that should not be hidden
const entityType = getEntityTypeFromHeader(entity)

const currentSelectedVersion = selected.get(entity.id)
const currentSelectedVersion = selected.get(
entity.id,
)?.targetVersionNumber
let versionNumber: number | undefined = undefined
if ('versionNumber' in entity) {
if (
currentSelectedVersion != null &&
currentSelectedVersion !== NO_VERSION_NUMBER
) {
if (currentSelectedVersion != null) {
// if a version is selected, the row should show that version's data
versionNumber = currentSelectedVersion
} else if (versionSelection === VersionSelectionType.REQUIRED) {
Expand Down Expand Up @@ -406,10 +397,7 @@ export const ChallengeDataTable: React.FunctionComponent<DetailsViewProps> = ({

toggleSelection({
targetId: id,
targetVersionNumber:
currentSelectedVersion === NO_VERSION_NUMBER
? undefined
: currentSelectedVersion,
targetVersionNumber: currentSelectedVersion,
})
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const EntityFileBrowser: React.FunctionComponent<
[setBreadcrumbsProps],
)
const projectId = entityBundle?.path.path[1].id ?? undefined
const emptyMap = Map<string, number>()
const emptyMap = Map<string, Reference>()
const types: EntityType[] = [
EntityType.FOLDER,
EntityType.FILE,
Expand All @@ -66,7 +66,7 @@ export const EntityFileBrowser: React.FunctionComponent<
},
}
return (
<div className="EntityFinderReflexContainer">
<div className="EntityFileBrowser EntityFinderReflexContainer">
<SizeMe>
{({ size }) => (
<ReflexContainer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import EntityFinder from './EntityFinder'
import { FinderScope } from './tree/EntityTree'
import { EntityType } from '@sage-bionetworks/synapse-types'
import { VersionSelectionType } from './VersionSelectionType'
import { fn } from '@storybook/test'

const meta = {
title: 'Synapse/EntityFinder',
Expand All @@ -24,37 +25,30 @@ export const DualPane: Story = {
args: {
treeOnly: false,
initialScope: FinderScope.CURRENT_PROJECT,
projectId: 'syn23567475',
initialContainer: 'syn24183903',
projectId: 'syn5550376',
initialContainer: 'syn5550376',
selectMultiple: true,
visibleTypesInList: Object.values(EntityType),
versionSelection: VersionSelectionType.TRACKED,
onSelectedChange: selected => {
console.log('Selection changed:', selected)
},
onSelectedChange: fn(),
selectableTypes: Object.values(EntityType),
selectedCopy: count => {
return `${count} Item${count > 1 ? 's' : ''} Selected`
},
},
}

export const SinglePane: Story = {
args: {
treeOnly: true,
initialScope: FinderScope.CURRENT_PROJECT,
projectId: 'syn23567475',
initialContainer: 'syn24183903',
projectId: 'syn5550376',
initialContainer: 'syn5550376',
selectMultiple: false,
visibleTypesInTree: [
EntityType.PROJECT,
EntityType.FOLDER,
EntityType.TABLE,
],
versionSelection: VersionSelectionType.DISALLOWED,
onSelectedChange: selected => {
console.log('Selection changed:', selected)
},
onSelectedChange: fn(),
selectableTypes: [EntityType.PROJECT, EntityType.FOLDER],
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
} from '../../../src/components/EntityFinder/details/EntityDetailsList'
import EntityFinder, {
EntityFinderProps,
NO_VERSION_NUMBER,
} from '../../../src/components/EntityFinder/EntityFinder'
import * as EntityTreeModule from '../../../src/components/EntityFinder/tree/EntityTree'
import { FinderScope } from '../../../src/components/EntityFinder/tree/EntityTree'
Expand All @@ -27,6 +26,7 @@ import {
import { Map } from 'immutable'
import * as useEntityBundleModule from '../../../src/synapse-queries/entity/useEntityBundle'
import SynapseClient from '../../../src/synapse-client'
import { getUseQuerySuccessMock } from '../../testutils/ReactQueryMockUtils'

jest.mock('react-reflex', () => {
return {
Expand All @@ -42,14 +42,14 @@ jest.mock('react-reflex', () => {

const mockUseGetEntityBundle = jest.spyOn(useEntityBundleModule, 'default')

const mockEntityTree = jest
jest
.spyOn(EntityTreeModule, 'EntityTree')
.mockImplementation(({ toggleSelection, setDetailsViewConfiguration }) => {
invokeToggleSelectionViaTree = reference => {
toggleSelection(reference)
toggleSelection!(reference)
}
invokeSetConfigViaTree = configuration => {
setDetailsViewConfiguration(configuration)
setDetailsViewConfiguration!(configuration)
}
return <div role="tree"></div>
})
Expand Down Expand Up @@ -85,30 +85,34 @@ const defaultProps: EntityFinderProps = {
visibleTypesInTree: [EntityType.PROJECT, EntityType.FOLDER],
selectableTypes: Object.values(EntityType),
treeOnly: false,
selectedCopy: 'Chosen Entities',
}

function renderComponent(propOverrides?: Partial<EntityFinderProps>) {
return render(
const user = userEvent.setup()
render(
<SynapseTestContext>
<EntityFinder {...defaultProps} {...propOverrides} />
</SynapseTestContext>,
)

const searchInput = screen.getByRole('textbox')

return { user, searchInput }
}

describe('EntityFinder tests', () => {
beforeEach(() => {
jest.clearAllMocks()

mockUseGetEntityBundle.mockReturnValue({
data: {
mockUseGetEntityBundle.mockReturnValue(
getUseQuerySuccessMock({
entity: {
id: 'syn123',
name: 'My file entity',
concreteType: 'org.sagebionetworks.repo.model.FileEntity',
},
},
})
}),
)
})

describe('single-select toggleSelection validation', () => {
Expand Down Expand Up @@ -328,7 +332,9 @@ describe('EntityFinder tests', () => {
await waitFor(() =>
expect(mockDetailsList).toHaveBeenLastCalledWith(
expect.objectContaining({
selected: Map([[reference.targetId, NO_VERSION_NUMBER]]),
selected: Map([
[reference.targetId, { targetId: reference.targetId }],
]),
}),
{},
),
Expand All @@ -355,61 +361,24 @@ describe('EntityFinder tests', () => {
})

describe('Search', () => {
it('Updates the search button text when only one type is selectable', async () => {
// Folders are the only selectable type, so only folders will appear in search.
renderComponent({ selectableTypes: [EntityType.FOLDER] })

// Search button text should match
await screen.findByText('Search for Folders')
})

it('Updates the search button text when only table types are selectable', async () => {
// Datasets and entity views are table types
renderComponent({
selectableTypes: [EntityType.DATASET, EntityType.ENTITY_VIEW],
it('handles searching for terms', async () => {
const { user, searchInput } = renderComponent({
treeOnly: true,
selectableTypes: [EntityType.FILE],
})

// Search button text should match
await screen.findByText('Search for Tables')
})

it('clicking the search button opens the input field', async () => {
renderComponent({ treeOnly: true })

// Tree should be visible before we start search. No table should be visible
expect(() => screen.getByRole('tree')).not.toThrowError()
expect(() => screen.getByRole('table')).toThrowError()

// Don't show the search box before the button is clicked
expect(() => screen.getByRole('textbox')).toThrowError()

await userEvent.click(screen.getByText('Search all of Synapse'))
await waitFor(() => screen.getByRole('textbox'))

// The tree should be hidden when searching. The table of search results should be visible
expect(() => screen.getByRole('tree')).toThrowError()
expect(() => screen.getByRole('table')).not.toThrowError()

// Close the search
await userEvent.click(screen.getByText('Back to Browse'))

// Tree should come back, table should be gone
await waitFor(() => screen.getByRole('tree'))
expect(() => screen.getByRole('table')).toThrowError()

// Search input field should be gone too
expect(() => screen.getByRole('textbox')).toThrowError()
})

it('handles searching for terms', async () => {
renderComponent({ selectableTypes: [EntityType.FILE] })
expect(() => screen.getByRole('tree')).not.toThrow()
expect(() => screen.getByRole('table')).toThrow()

const query = 'my search terms '
const queryTerms = ['my', 'search', 'terms']
await userEvent.click(screen.getByText('Search for Files'))
await waitFor(() => screen.getByRole('textbox'))
await userEvent.type(screen.getByRole('textbox'), query)
await userEvent.type(screen.getByRole('textbox'), '{enter}')
await user.type(searchInput, query)
await user.type(searchInput, '{enter}')

// The tree should be hidden when searching. The table of search results should be visible
expect(() => screen.getByRole('tree')).toThrow()
expect(() => screen.getByRole('table')).not.toThrow()

await waitFor(() =>
expect(mockDetailsList).toBeCalledWith(
Expand Down Expand Up @@ -453,19 +422,51 @@ describe('EntityFinder tests', () => {
{},
),
)

// Clear the search results and verify we go back to the original view
await user.click(screen.getByRole('button', { name: 'Clear Search' }))

// Tree should be visible once again and table should be hidden
expect(() => screen.getByRole('tree')).not.toThrow()
expect(() => screen.getByRole('table')).toThrow()
})

it('handles searching for a synId', async () => {
renderComponent()
const { user, searchInput } = renderComponent()

const entityId = 'syn123'
const version = 2

const entityHeaderResult = { results: [{ id: entityId }] }
const entityHeaderResultWithVersion: PaginatedResults<
Partial<EntityHeader>
> = {
results: [{ id: entityId, versionNumber: version }],
const entityHeaderResult: PaginatedResults<EntityHeader> = {
results: [
{
id: entityId,
name: 'foo',
benefactorId: 123,
type: 'org.sagebionetworks.repo.model.FileEntity',
createdOn: '',
modifiedOn: '',
createdBy: '',
modifiedBy: '',
isLatestVersion: false,
},
],
}
const entityHeaderResultWithVersion: PaginatedResults<EntityHeader> = {
results: [
{
id: entityId,
versionNumber: version,
name: 'foo',
benefactorId: 123,
type: 'org.sagebionetworks.repo.model.FileEntity',
createdOn: '',
modifiedOn: '',
createdBy: '',
modifiedBy: '',
isLatestVersion: true,
},
],
}

when(mockGetEntityHeaders)
Expand All @@ -479,10 +480,8 @@ describe('EntityFinder tests', () => {
)
.mockResolvedValue(entityHeaderResultWithVersion)

await userEvent.click(screen.getByText('Search all of Synapse'))
await waitFor(() => screen.getByRole('textbox'))
await userEvent.type(screen.getByRole('textbox'), entityId)
await userEvent.type(screen.getByRole('textbox'), '{enter}')
await user.type(searchInput, entityId)
await user.type(searchInput, '{enter}')

await waitFor(() =>
expect(mockDetailsList).toBeCalledWith(
Expand All @@ -498,12 +497,9 @@ describe('EntityFinder tests', () => {
expect(mockGetEntityHeaders).toBeCalledTimes(1)

// Search with a version number
await userEvent.clear(screen.getByRole('textbox'))
await userEvent.type(
screen.getByRole('textbox'),
`${entityId}.${version}`,
)
await userEvent.type(screen.getByRole('textbox'), '{enter}')
await user.clear(searchInput)
await user.type(searchInput, `${entityId}.${version}`)
await user.type(searchInput, '{enter}')
await waitFor(() =>
expect(mockDetailsList).toBeCalledWith(
expect.objectContaining({
Expand Down
Loading

0 comments on commit 9cb7ad9

Please sign in to comment.