Skip to content

Commit

Permalink
Classifier tasks: Add SubjectViewerStore to task stories (#2031)
Browse files Browse the repository at this point in the history
Rewrite all task stories as CSF. Move common task story code into a `MockTask` component, which takes a `tasks` object and renders it in a `Tasks` component, with a classifier store. Adds `@stories` as an alias for storybook helper code and `@components` as an alias for the root of the Classify components tree. Fixes a small bug where feature detection helpers weren't being exported properly.
  • Loading branch information
eatyourgreens authored Feb 18, 2021
1 parent 1cc8ca1 commit c09f091
Show file tree
Hide file tree
Showing 17 changed files with 559 additions and 977 deletions.
Original file line number Diff line number Diff line change
@@ -1,144 +1,75 @@
import { withKnobs, boolean, radios, select } from '@storybook/addon-knobs'
import asyncStates from '@zooniverse/async-states'
import { storiesOf } from '@storybook/react'
import zooTheme from '@zooniverse/grommet-theme'
import { types } from 'mobx-state-tree'
import React from 'react'
import { Box, Grommet } from 'grommet'
import { Provider } from 'mobx-react'
import { Tasks } from './Tasks'
import ClassificationStore from '@store/ClassificationStore'
import SubjectStore from '@store/SubjectStore'
import WorkflowStore from '@store/WorkflowStore'
import WorkflowStepStore from '@store/WorkflowStepStore'
import { MockTask } from '@stories/components'
import Tasks from './Tasks'

function createStore() {
const classifications = ClassificationStore.create()
const mockSubject = {
id: 'subject',
metadata: {}
}
const mockWorkflow = {
id: 'workflow',
version: '1.0'
}
const mockProject = {
id: 'project'
export default {
title: 'Tasks / General',
component: Tasks,
args: {
dark: false,
isThereTaskHelp: true,
required: false,
subjectReadyState: asyncStates.success
},
argTypes: {
subjectReadyState: {
control: {
type: 'select',
options: asyncStates
}
}
},
parameters: {
viewport: {
defaultViewport: 'responsive'
}
}

const store = types.model('MockStore', {
classifications: ClassificationStore,
subjects: SubjectStore,
workflows: WorkflowStore,
workflowSteps: WorkflowStepStore
})
.create({
classifications,
subjects: SubjectStore.create({}),
workflows: WorkflowStore.create({}),
workflowSteps: WorkflowStepStore.create({})
})
classifications.createClassification(mockSubject, mockWorkflow, mockProject)
return store
}

const store = createStore()
function addStepToStore (step, tasks) {
const steps = [ ['S1', step] ]
store.workflowSteps.setStepsAndTasks({ steps, tasks })
store.workflowSteps.active.tasks.forEach(task => store.classifications.addAnnotation(task))
export function Loading({ dark, isThereTaskHelp, required, subjectReadyState }) {
return (
<MockTask
dark={dark}
loadingState={asyncStates.loading}
/>
)
}

function MockTask (props) {
const { dark, ...taskProps } = props

export function Error({ dark, isThereTaskHelp, required, subjectReadyState }) {
return (
<Grommet
background={{
dark: 'dark-1',
light: 'light-1'
}}
theme={Object.assign({}, zooTheme, { dark })}
themeMode={(dark) ? 'dark' : 'light'}
>
<Box
background={{
dark: 'dark-3',
light: 'neutral-6'
}}
pad='1em'
width='380px'
>
<Tasks
{...taskProps}
/>
</Box>
</Grommet>
<MockTask
dark={dark}
loadingState={asyncStates.error}
/>
)
}

storiesOf('Tasks / General', module)
.addDecorator(withKnobs)
.addParameters({
viewport: {
defaultViewport: 'responsive'
}
})
.add('loading', function () {
return (
<Provider classifierStore={{}}>
<MockTask
loadingState={asyncStates.loading}
/>
</Provider>
)
})
.add('error', function () {
return (
<Provider classifierStore={{}}>
<MockTask
loadingState={asyncStates.error}
/>
</Provider>
)
})
.add('multiple tasks', function () {
const tasks = {
init: {
answers: [{ label: 'yes' }, { label: 'no' }],
help: 'Choose an answer from the choices given, then press Done.',
question: 'Is there a cat?',
required: radios('Required', { true: 'true', false: '' }, ''),
taskKey: 'init',
type: 'single'
},
T1: {
answers: [{ label: 'sleeping' }, { label: 'playing' }, { label: 'looking indifferent' }],
help: 'Pick as many answers as apply, then press Done.',
question: 'What is it doing?',
required: radios('Required', { true: 'true', false: '' }, ''),
taskKey: 'T1',
type: 'multiple'
}
export function MultipleTasks({ dark, isThereTaskHelp, required, subjectReadyState }) {
const tasks = {
init: {
answers: [{ label: 'yes' }, { label: 'no' }],
help: 'Choose an answer from the choices given, then press Done.',
question: 'Is there a cat?',
required,
taskKey: 'init',
type: 'single'
},
T1: {
answers: [{ label: 'sleeping' }, { label: 'playing' }, { label: 'looking indifferent' }],
help: 'Pick as many answers as apply, then press Done.',
question: 'What is it doing?',
required,
taskKey: 'T1',
type: 'multiple'
}
const step = {
stepKey: 'S1',
taskKeys: ['init', 'T1']
}
addStepToStore(step, tasks)
const dark = boolean('Dark theme', false)
const subjectReadyState = select('Subject loading', asyncStates, asyncStates.success)
const isThereTaskHelp = boolean('Enable task help', true)
return (
<Provider classifierStore={store}>
<MockTask
dark={dark}
classification={store.classifications.active}
isThereTaskHelp={isThereTaskHelp}
loadingState={asyncStates.success}
step={store.workflowSteps.active}
subjectReadyState={subjectReadyState}
/>
</Provider>
)
})
}
return (
<MockTask
dark={dark}
isThereTaskHelp={isThereTaskHelp}
subjectReadyState={subjectReadyState}
tasks={tasks}
/>
)
}
5 changes: 3 additions & 2 deletions packages/lib-classifier/src/helpers/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as featureDetection from './featureDetection'
export { default as createLocationCounts } from './createLocationCounts'
export { default as featureDetection } from './featureDetection'
export { featureDetection }
export { default as findLocationsByMediaType } from './findLocationsByMediaType'
export { default as layouts } from './layouts'
export { default as shownMarks } from './shownMarks'
export { default as subjectViewers } from './subjectViewers'
export { default as validateMimeType } from './validateMimeType'
export { default as validateSubjectLocations } from './validateSubjectLocations'
export { default as shownMarks } from './shownMarks'
Original file line number Diff line number Diff line change
@@ -1,122 +1,54 @@
import { withKnobs, boolean, select } from '@storybook/addon-knobs'
import asyncStates from '@zooniverse/async-states'
import { storiesOf } from '@storybook/react'
import zooTheme from '@zooniverse/grommet-theme'
import { types } from 'mobx-state-tree'
import React from 'react'
import { Box, Grommet } from 'grommet'
import { Provider } from 'mobx-react'
import { Tasks } from '../../../../components/Classifier/components/TaskArea/components/Tasks/Tasks'
import ClassificationStore from '@store/ClassificationStore'
import SubjectStore from '@store/SubjectStore'
import WorkflowStore from '@store/WorkflowStore'
import WorkflowStepStore from '@store/WorkflowStepStore'
import { MockTask } from '@stories/components'
import DataVisAnnotationTask from './DataVisAnnotationTask'

function createStore() {
const classifications = ClassificationStore.create()
const mockSubject = {
id: 'subject',
metadata: {}
}
const mockWorkflow = {
id: 'workflow',
version: '1.0'
}
const mockProject = {
id: 'project'
export default {
title: 'Tasks / Data Visualization Annotation',
component: DataVisAnnotationTask,
args: {
dark: false,
isThereTaskHelp: true,
required: false,
subjectReadyState: asyncStates.success
},
argTypes: {
subjectReadyState: {
control: {
type: 'select',
options: asyncStates
}
}
},
parameters: {
viewport: {
defaultViewport: 'responsive'
}
}

const store = types.model('MockStore', {
classifications: ClassificationStore,
subjects: SubjectStore,
workflows: WorkflowStore,
workflowSteps: WorkflowStepStore
})
.create({
classifications,
subjects: SubjectStore.create({}),
workflows: WorkflowStore.create({}),
workflowSteps: WorkflowStepStore.create({})
})
classifications.createClassification(mockSubject, mockWorkflow, mockProject)
return store
}

const store = createStore()
function addStepToStore(step, tasks) {
const steps = [['S1', step]]
store.workflowSteps.setStepsAndTasks({ steps, tasks })
store.workflowSteps.active.tasks.forEach(task => store.classifications.addAnnotation(task))
}

function MockTask(props) {
const { dark, ...taskProps } = props

export function LightTheme({ dark, isThereTaskHelp, required, subjectReadyState }) {
const tasks = {
T4: {
instruction: 'Do you spot a transit?',
required,
taskKey: 'T4',
tools: [
{
help: '',
label: 'Transit?',
type: 'graph2dRangeX'
}
],
type: 'dataVisAnnotation'
}
}
return (
<Grommet
background={{
dark: 'dark-1',
light: 'light-1'
}}
theme={Object.assign({}, zooTheme, { dark })}
themeMode={(dark) ? 'dark' : 'light'}
>
<Box
background={{
dark: 'dark-3',
light: 'neutral-6'
}}
pad='1em'
width='380px'
>
<Tasks
{...taskProps}
/>
</Box>
</Grommet>
<MockTask
dark={dark}
isThereTaskHelp={isThereTaskHelp}
subjectReadyState={subjectReadyState}
tasks={tasks}
/>
)
}

storiesOf('Tasks / Data Visualization Annotation', module)
.addDecorator(withKnobs)
.addParameters({
viewport: {
defaultViewport: 'responsive'
}
})
.add('light theme', function () {
const tasks = {
T4: {
instruction: 'Do you spot a transit?',
taskKey: 'T4',
tools: [
{
help: '',
label: 'Transit?',
type: 'graph2dRangeX'
}
],
type: 'dataVisAnnotation'
}
}
const step = {
stepKey: 'S1',
taskKeys: ['T4']
}
addStepToStore(step, tasks)
const dark = boolean('Dark theme', false)
const subjectReadyState = select('Subject loading', asyncStates, asyncStates.success)
const isThereTaskHelp = boolean('Enable task help', true)
return (
<Provider classifierStore={store}>
<MockTask
dark={dark}
classification={store.classifications.active}
isThereTaskHelp={isThereTaskHelp}
loadingState={asyncStates.success}
step={store.workflowSteps.active}
subjectReadyState={subjectReadyState}
/>
</Provider>
)
})
Loading

0 comments on commit c09f091

Please sign in to comment.