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

Engaging Crowds: Add URL workflow and subject set to WorkflowMenu #2169

Merged
merged 7 commits into from
Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 23 additions & 5 deletions packages/app-project/src/screens/ClassifyPage/ClassifyPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import StandardLayout from '@shared/components/StandardLayout'
import WorkflowAssignmentModal from './components/WorkflowAssignmentModal'
import WorkflowMenu from './components/WorkflowMenu'

const ClassifierWrapper = dynamic(() =>
export const ClassifierWrapper = dynamic(() =>
import('./components/ClassifierWrapper'), { ssr: false }
)

Expand All @@ -32,7 +32,25 @@ function ClassifyPage ({
: ['1em', 'auto', '1em']

const [ workflowFromUrl ] = workflows.filter(workflow => workflow.id === workflowID)
const canClassify = workflowFromUrl?.grouped ? !!subjectSetID : !!workflowID
let subjectSetFromUrl
if (workflowFromUrl && workflowFromUrl.subjectSets) {
[ subjectSetFromUrl ] = workflowFromUrl.subjectSets.filter(subjectSet => subjectSet.id === subjectSetID)
}
// The classifier requires a workflow by default
let canClassify = !!workflowID
// grouped workflows require a subject set
canClassify = workflowFromUrl?.grouped ? !!subjectSetID : canClassify
// indexed subject sets require a subject
canClassify = subjectSetFromUrl?.isIndexed ? !!subjectID : canClassify

let classifierProps = {}
if (canClassify) {
classifierProps = {
workflowID,
subjectSetID,
subjectID
}
}

return (
<StandardLayout>
Expand All @@ -46,16 +64,16 @@ function ClassifyPage ({
<Box as='main' fill='horizontal'>
{!canClassify && (
<WorkflowMenu
subjectSetFromUrl={subjectSetFromUrl}
workflowFromUrl={workflowFromUrl}
workflows={workflows}
/>
)}
<Grid columns={responsiveColumns} gap='small'>
<ProjectName />
<ClassifierWrapper
onAddToCollection={addToCollection}
subjectID={subjectID}
subjectSetID={subjectSetID}
workflowID={workflowID}
{...classifierProps}
/>
<ThemeModeToggle />
</Grid>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { shallow } from 'enzyme'
import React from 'react'

import { ClassifyPage } from './ClassifyPage'
import { ClassifyPage, ClassifierWrapper } from './ClassifyPage'
import FinishedForTheDay from './components/FinishedForTheDay'
import WorkflowMenu from './components/WorkflowMenu'
import ThemeModeToggle from '@components/ThemeModeToggle'
Expand Down Expand Up @@ -84,6 +84,11 @@ describe('Component > ClassifyPage', function () {
it('should show a workflow menu', function () {
expect(wrapper.find(WorkflowMenu)).to.have.lengthOf(1)
})

it('should not pass the workflow ID to the classifier', function () {
const classifier = wrapper.find(ClassifierWrapper)
expect(classifier.prop('workflowID')).to.be.undefined()
})
})

describe('with a subject set', function () {
Expand All @@ -100,6 +105,90 @@ describe('Component > ClassifyPage', function () {
it('should not show a workflow menu', function () {
expect(wrapper.find(WorkflowMenu)).to.have.lengthOf(0)
})

it('should pass the workflow ID to the classifier', function () {
const classifier = wrapper.find(ClassifierWrapper)
expect(classifier.prop('workflowID')).to.equal('1234')
})

it('should pass the subject set ID to the classifier', function () {
const classifier = wrapper.find(ClassifierWrapper)
expect(classifier.prop('subjectSetID')).to.equal('3456')
})
})

describe('with an indexed subject set', function () {
let workflows = [{
id: '1234',
grouped: true,
subjectSets: [{
id: '3456',
displayName: 'indexed set',
isIndexed: true
}]
}]

describe('without a subject', function () {
let wrapper

before(function () {
wrapper = shallow(
<ClassifyPage
subjectSetID='3456'
workflowID='1234'
workflows={workflows}
/>
)
})

it('should show a workflow menu', function () {
expect(wrapper.find(WorkflowMenu)).to.have.lengthOf(1)
})

it('should not pass the workflow ID to the classifier', function () {
const classifier = wrapper.find(ClassifierWrapper)
expect(classifier.prop('workflowID')).to.be.undefined()
})

it('should not pass the subject set ID to the classifier', function () {
const classifier = wrapper.find(ClassifierWrapper)
expect(classifier.prop('subjectSetID')).to.be.undefined()
})
})

describe('with a subject', function () {
let wrapper

before(function () {
wrapper = shallow(
<ClassifyPage
subjectID='5678'
subjectSetID='3456'
workflowID='1234'
workflows={workflows}
/>
)
})

it('should not show a workflow menu', function () {
expect(wrapper.find(WorkflowMenu)).to.have.lengthOf(0)
})

it('should pass the workflow ID to the classifier', function () {
const classifier = wrapper.find(ClassifierWrapper)
expect(classifier.prop('workflowID')).to.equal('1234')
})

it('should pass the subject set ID to the classifier', function () {
const classifier = wrapper.find(ClassifierWrapper)
expect(classifier.prop('subjectSetID')).to.equal('3456')
})

it('should pass the subject ID to the classifier', function () {
const classifier = wrapper.find(ClassifierWrapper)
expect(classifier.prop('subjectID')).to.equal('5678')
})
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import SubjectPicker from '@shared/components/SubjectPicker'
*/
export default function WorkflowMenu({
headingBackground = 'brand',
subjectSetFromUrl,
titleColor = 'neutral-6',
workflowFromUrl,
workflows
}) {
const router = useRouter()
const { owner, project } = router?.query || {}
const [ activeSubjectSet, setActiveSubjectSet ] = useState()
const [ activeSubjectSet, setActiveSubjectSet ] = useState(subjectSetFromUrl)
const [ activeWorkflow, setActiveWorkflow ] = useState(workflowFromUrl)

function onSelectSubjectSet(event, subjectSet) {
Expand Down Expand Up @@ -84,6 +85,11 @@ export default function WorkflowMenu({
)
}

const subjectSetType = shape({
displayName: string,
id: string
})

const workflowType = shape({
displayName: string,
id: string
Expand All @@ -94,6 +100,10 @@ WorkflowMenu.propTypes = {
Background colour of the title bar.
*/
headingBackground: string,
/**
An optional selected subject set. If present, we can jump straight to subject selection.
**/
subjectSetFromUrl: subjectSetType,
/**
text colour of the title bar.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import WorkflowMenu from './'

import WorkflowSelector from '@shared/components/WorkflowSelector'
import SubjectSetPicker from '@shared/components/SubjectSetPicker'
import SubjectPicker from '@shared/components/SubjectPicker'

describe('Component > ClassifyPage > WorkflowMenu', function () {
describe('without a selected workflow', function () {
Expand Down Expand Up @@ -41,4 +42,34 @@ describe('Component > ClassifyPage > WorkflowMenu', function () {
expect(wrapper.find(SubjectSetPicker)).to.have.lengthOf(1)
})
})

describe('with a selected workflow and subject set', function () {
let wrapper
let workflows = [{
id: '1234',
grouped: true
}]
let subjectSet = {
id: '345',
displayName: 'test set'
}

before(function () {
wrapper = shallow(
<WorkflowMenu
subjectSetFromUrl={subjectSet}
workflowFromUrl={workflows[0]}
workflows={workflows}
/>
)
})

it('should not show a workflow selector', function () {
expect(wrapper.find(WorkflowSelector)).to.have.lengthOf(0)
})

it('should show a subject picker', function () {
expect(wrapper.find(SubjectPicker)).to.have.lengthOf(1)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@ function BackButton({ onClick }) {
/>
)
}
function SubjectSetPicker ({ baseUrl, onClose, onSelect, workflow }) {
/**
Display a list of subject set cards for a workflow. Each card links to the corresponding subject set ID.
*/
function SubjectSetPicker ({
baseUrl,
onClose = () => true,
onSelect = () => true,
workflow
}) {
const router = useRouter()
/*
Vertical spacing for the picker instructions.
Expand Down Expand Up @@ -100,9 +108,21 @@ function SubjectSetPicker ({ baseUrl, onClose, onSelect, workflow }) {
}

SubjectSetPicker.propTypes = {
/**
Base URL for links eg. `/projects/${owner}/${project}/classify`
*/
baseUrl: string.isRequired,
/**
Callback to close/cancel the picker without taking action.
*/
onClose: func,
owner: string.isRequired,
project: string.isRequired,
/**
Callback to call on clicking a subject set card: `onSelect(event, subjectSet)`
*/
onSelect: func,
/**
The selected workflow.
*/
workflow: shape({
completeness: number,
default: bool,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@ import getConfig from 'next/config'
import { array, number, string } from 'prop-types'
import React from 'react'

function SubjectSetCard (props) {
const { availableSubjects, display_name, id, set_member_subjects_count, subjects } = props
/**
Summary card for a subject set, showing a preview subject, the set name, total subject count and completeness percentage.
*/
function SubjectSetCard ({
availableSubjects,
display_name,
id,
set_member_subjects_count,
subjects = []
}) {
const [subject] = subjects
const { publicRuntimeConfig = {} } = getConfig() || {}
const assetPrefix = publicRuntimeConfig.assetPrefix || ''
Expand Down Expand Up @@ -69,14 +77,26 @@ function SubjectSetCard (props) {
}

SubjectSetCard.propTypes = {
display_name: string.required,
id: string.required,
set_member_subjects_count: number.required,
/**
The number of subjects available to be classified.
*/
availableSubjects: number.isRequired,
/**
Subject set title.
*/
display_name: string.isRequired,
/**
Subject set ID.
*/
id: string.isRequired,
/**
Total subject count
*/
set_member_subjects_count: number.isRequired,
/**
Preview subjects. Used to show a preview image.
*/
subjects: array
}

SubjectSetCard.defaultProps = {
subjects: []
}

export default SubjectSetCard