From c09f0912c67a6815ab167d5bab32160080789c68 Mon Sep 17 00:00:00 2001 From: Jim O'Donnell Date: Thu, 18 Feb 2021 15:34:19 +0000 Subject: [PATCH] Classifier tasks: Add SubjectViewerStore to task stories (#2031) 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. --- .../components/Tasks/Tasks.stories.js | 193 ++++++----------- packages/lib-classifier/src/helpers/index.js | 5 +- .../DataVisAnnotationTask.stories.js | 158 ++++---------- .../components/DrawingTask.stories.js | 205 ++++++------------ .../components/MultipleChoiceTask.stories.js | 151 ++++--------- .../components/SimpleDropdownTask.stories.js | 167 ++++---------- .../components/SingleChoiceTask.stories.js | 152 ++++--------- .../components/TextTask/TextTask.stories.js | 200 +++++------------ .../TranscriptionTask.stories.js | 160 ++++---------- .../store/helpers/createStore/createStore.js | 8 + .../src/store/helpers/createStore/index.js | 1 + .../lib-classifier/src/store/helpers/index.js | 1 + .../stories/components/MockTask/MockTask.js | 128 +++++++++++ .../src/stories/components/MockTask/index.js | 1 + .../src/stories/components/index.js | 1 + packages/lib-classifier/webpack.dev.js | 2 + packages/lib-classifier/webpack.dist.js | 3 + 17 files changed, 559 insertions(+), 977 deletions(-) create mode 100644 packages/lib-classifier/src/store/helpers/createStore/createStore.js create mode 100644 packages/lib-classifier/src/store/helpers/createStore/index.js create mode 100644 packages/lib-classifier/src/store/helpers/index.js create mode 100644 packages/lib-classifier/src/stories/components/MockTask/MockTask.js create mode 100644 packages/lib-classifier/src/stories/components/MockTask/index.js create mode 100644 packages/lib-classifier/src/stories/components/index.js diff --git a/packages/lib-classifier/src/components/Classifier/components/TaskArea/components/Tasks/Tasks.stories.js b/packages/lib-classifier/src/components/Classifier/components/TaskArea/components/Tasks/Tasks.stories.js index 588baaa428..ebda6e7daa 100644 --- a/packages/lib-classifier/src/components/Classifier/components/TaskArea/components/Tasks/Tasks.stories.js +++ b/packages/lib-classifier/src/components/Classifier/components/TaskArea/components/Tasks/Tasks.stories.js @@ -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 ( + + ) } -function MockTask (props) { - const { dark, ...taskProps } = props - +export function Error({ dark, isThereTaskHelp, required, subjectReadyState }) { return ( - - - - - + ) } -storiesOf('Tasks / General', module) - .addDecorator(withKnobs) - .addParameters({ - viewport: { - defaultViewport: 'responsive' - } - }) - .add('loading', function () { - return ( - - - - ) - }) - .add('error', function () { - return ( - - - - ) - }) - .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 ( - - - - ) - }) \ No newline at end of file + } + return ( + + ) +} \ No newline at end of file diff --git a/packages/lib-classifier/src/helpers/index.js b/packages/lib-classifier/src/helpers/index.js index aad629b0e4..13ace72e99 100644 --- a/packages/lib-classifier/src/helpers/index.js +++ b/packages/lib-classifier/src/helpers/index.js @@ -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' diff --git a/packages/lib-classifier/src/plugins/tasks/DataVisAnnotationTask/components/DataVisAnnotationTask.stories.js b/packages/lib-classifier/src/plugins/tasks/DataVisAnnotationTask/components/DataVisAnnotationTask.stories.js index 0bd3f96c9a..4ddee48f07 100644 --- a/packages/lib-classifier/src/plugins/tasks/DataVisAnnotationTask/components/DataVisAnnotationTask.stories.js +++ b/packages/lib-classifier/src/plugins/tasks/DataVisAnnotationTask/components/DataVisAnnotationTask.stories.js @@ -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 ( - - - - - + ) } - -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 ( - - - - ) - }) \ No newline at end of file diff --git a/packages/lib-classifier/src/plugins/tasks/DrawingTask/components/DrawingTask.stories.js b/packages/lib-classifier/src/plugins/tasks/DrawingTask/components/DrawingTask.stories.js index e91de7ff84..5f0ea1db38 100644 --- a/packages/lib-classifier/src/plugins/tasks/DrawingTask/components/DrawingTask.stories.js +++ b/packages/lib-classifier/src/plugins/tasks/DrawingTask/components/DrawingTask.stories.js @@ -1,146 +1,79 @@ -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 DrawingTask from './DrawingTask' -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 / Drawing ', + component: DrawingTask, + 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 Drawing({ dark, isThereTaskHelp, required, subjectReadyState }) { + const tasks = { + T2: { + help: 'Draw on the image.', + instruction: 'Draw something', + required, + taskKey: 'T2', + tools: [ + { + color: zooTheme.global.colors['drawing-red'], + help: '', + label: 'Draw a line', + type: 'line' + }, { + color: zooTheme.global.colors['drawing-blue'], + help: '', + label: 'Point please.', + min: 1, + max: 2, + type: 'point', + }, { + color: zooTheme.global.colors['drawing-green'], + help: '', + label: 'Draw under the text', + type: 'transcriptionLine' + }, { + color: zooTheme.global.colors['drawing-orange'], + help: '', + label: 'Draw a rectangle', + type: 'rectangle', + }, { + color: zooTheme.global.colors['drawing-yellow'], + help: '', + label: 'Draw an ellipse', + type: 'ellipse', + } + ], + type: 'drawing' + } + } return ( - - - - - + ) } - -storiesOf('Tasks / Drawing Task', module) - .addDecorator(withKnobs) - .addParameters({ - viewport: { - defaultViewport: 'responsive' - } - }) - .add('drawing', function () { - const tasks = { - T2: { - help: 'Draw on the image.', - instruction: 'Draw something', - taskKey: 'T2', - tools: [ - { - color: zooTheme.global.colors['drawing-red'], - help: '', - label: 'Draw a line', - type: 'line' - }, { - color: zooTheme.global.colors['drawing-blue'], - help: '', - label: 'Point please.', - min: 1, - max: 2, - type: 'point', - }, { - color: zooTheme.global.colors['drawing-green'], - help: '', - label: 'Draw under the text', - type: 'transcriptionLine' - }, { - color: zooTheme.global.colors['drawing-orange'], - help: '', - label: 'Draw a rectangle', - type: 'rectangle', - }, { - color: zooTheme.global.colors['drawing-yellow'], - help: '', - label: 'Draw an ellipse', - type: 'ellipse', - } - ], - type: 'drawing' - } - } - const step = { - stepKey: 'S1', - taskKeys: ['T2'] - } - 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 ( - - - - ) - }) \ No newline at end of file diff --git a/packages/lib-classifier/src/plugins/tasks/MultipleChoiceTask/components/MultipleChoiceTask.stories.js b/packages/lib-classifier/src/plugins/tasks/MultipleChoiceTask/components/MultipleChoiceTask.stories.js index f2a09adb5e..8c78ad4552 100644 --- a/packages/lib-classifier/src/plugins/tasks/MultipleChoiceTask/components/MultipleChoiceTask.stories.js +++ b/packages/lib-classifier/src/plugins/tasks/MultipleChoiceTask/components/MultipleChoiceTask.stories.js @@ -1,120 +1,49 @@ -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, observer } 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 MultipleChoiceTask from './MultipleChoiceTask' -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 / Multiple Choice Question', + component: MultipleChoiceTask, + 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)) -} - -const ObservedTasks = observer(Tasks) - -function MockTask(props) { - const { dark, ...taskProps } = props - +export function LightTheme({ dark, isThereTaskHelp, required, subjectReadyState }) { + const tasks = { + 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' + } + } return ( - - - - - + ) } - -storiesOf('Tasks / Multiple Choice Question', module) - .addDecorator(withKnobs) - .addParameters({ - viewport: { - defaultViewport: 'responsive' - } - }) -.add('light theme', function () { - const tasks = { - 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' - } - } - const step = { - stepKey: 'S1', - taskKeys: ['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 ( - - - - ) - }) \ No newline at end of file diff --git a/packages/lib-classifier/src/plugins/tasks/SimpleDropdownTask/components/SimpleDropdownTask.stories.js b/packages/lib-classifier/src/plugins/tasks/SimpleDropdownTask/components/SimpleDropdownTask.stories.js index 45c792b368..e406c51ed6 100644 --- a/packages/lib-classifier/src/plugins/tasks/SimpleDropdownTask/components/SimpleDropdownTask.stories.js +++ b/packages/lib-classifier/src/plugins/tasks/SimpleDropdownTask/components/SimpleDropdownTask.stories.js @@ -1,128 +1,57 @@ -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, observer } 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 SimpleDropdownTask from './SimpleDropdownTask' -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 / Simple Dropdown', + component: SimpleDropdownTask, + 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)) -} - -const ObservedTasks = observer(Tasks) - -function MockTask(props) { - const { dark, ...taskProps } = props - +export function LightTheme({ dark, isThereTaskHelp, required, subjectReadyState }) { + const simpleDropdownTask = { + instruction: 'Choose your favourite colour', + allowCreate: false, + options: [ + 'Red', + 'Blue', + 'Yellow', + 'Green', + 'White', + 'Black', + ], + required, + taskKey: 'init', + type: 'dropdown-simple', + } + const tasks = { + init: simpleDropdownTask + } return ( - - - - - + ) } - -storiesOf('Tasks / Simple Dropdown Task', module) - .addDecorator(withKnobs) - .addParameters({ - viewport: { - defaultViewport: 'responsive' - } - }) - .add('light theme', function () { - const simpleDropdownTask = { - instruction: 'Choose your favourite colour', - allowCreate: false, - options: [ - 'Red', - 'Blue', - 'Yellow', - 'Green', - 'White', - 'Black', - ], - required: radios('Required', { true: 'true', false: '' }, ''), - taskKey: 'init', - type: 'dropdown-simple', - } - const tasks = { - init: simpleDropdownTask - } - const step = { - stepKey: 'S1', - taskKeys: ['init'] - } - 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 ( - - - - ) - }) diff --git a/packages/lib-classifier/src/plugins/tasks/SingleChoiceTask/components/SingleChoiceTask.stories.js b/packages/lib-classifier/src/plugins/tasks/SingleChoiceTask/components/SingleChoiceTask.stories.js index 07c65ac419..8dad70a7a7 100644 --- a/packages/lib-classifier/src/plugins/tasks/SingleChoiceTask/components/SingleChoiceTask.stories.js +++ b/packages/lib-classifier/src/plugins/tasks/SingleChoiceTask/components/SingleChoiceTask.stories.js @@ -1,119 +1,49 @@ -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, observer } 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 SingleChoiceTask from './SingleChoiceTask' -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 / Single Choice Question', + component: SingleChoiceTask, + 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)) -} - -const ObservedTasks = observer(Tasks) - -function MockTask(props) { - const { dark, ...taskProps } = props - +export function LightTheme({ 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' + } + } return ( - - - - - + ) -} -storiesOf('Tasks / Single Choice Question', module) - .addDecorator(withKnobs) - .addParameters({ - viewport: { - defaultViewport: 'responsive' - } - }) - .add('light theme', 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' - } - } - const step = { - stepKey: 'S1', - taskKeys: ['init'] - } - 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 ( - - - - ) - }) \ No newline at end of file +} \ No newline at end of file diff --git a/packages/lib-classifier/src/plugins/tasks/TextTask/components/TextTask/TextTask.stories.js b/packages/lib-classifier/src/plugins/tasks/TextTask/components/TextTask/TextTask.stories.js index 7eddc0bfab..b32599502f 100644 --- a/packages/lib-classifier/src/plugins/tasks/TextTask/components/TextTask/TextTask.stories.js +++ b/packages/lib-classifier/src/plugins/tasks/TextTask/components/TextTask/TextTask.stories.js @@ -1,151 +1,71 @@ -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, observer } 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 TextTask from './TextTask' -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 / Text', + component: TextTask, + 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)) } -const ObservedTasks = observer(Tasks) - -function MockTask(props) { - const { dark, ...taskProps } = props - +export function Default({ dark, isThereTaskHelp, required, subjectReadyState }) { + const tasks = { + T0: { + help: 'Type something into the text box.', + instruction: 'Type something here', + required, + taskKey: 'T0', + text_tags: ['insertion', 'deletion'], + type: 'text' + } + } return ( - - - - - + ) } -storiesOf('Tasks / Text', module) - .addDecorator(withKnobs) - .addParameters({ - viewport: { - defaultViewport: 'responsive' - } - }) -.add('default', function () { - const tasks = { - T0: { - help: 'Type something into the text box.', - instruction: 'Type something here', - taskKey: 'T0', - text_tags: ['insertion', 'deletion'], - type: 'text' - } - } - const step = { - stepKey: 'S1', - taskKeys: ['T0'] +export function withSuggestions({ dark, isThereTaskHelp, required, subjectReadyState }) { + const tasks = { + T0: { + help: 'Type something into the text box.', + instruction: 'Type something here', + required, + taskKey: 'T0', + text_tags: ['insertion', 'deletion'], + type: 'text' } - 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 ( - - - - ) - }) - .add('with suggestions', function () { - const tasks = { - T0: { - help: 'Type something into the text box.', - instruction: 'Type something here', - taskKey: 'T0', - text_tags: ['insertion', 'deletion'], - type: 'text' - } - } - const step = { - stepKey: 'S1', - taskKeys: ['T0'] - } - 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 ( - - - - ) - }) \ No newline at end of file + } + return ( + + ) +} \ No newline at end of file diff --git a/packages/lib-classifier/src/plugins/tasks/experimental/TranscriptionTask/TranscriptionTask.stories.js b/packages/lib-classifier/src/plugins/tasks/experimental/TranscriptionTask/TranscriptionTask.stories.js index 5a88b30a01..e0ba7f09a1 100644 --- a/packages/lib-classifier/src/plugins/tasks/experimental/TranscriptionTask/TranscriptionTask.stories.js +++ b/packages/lib-classifier/src/plugins/tasks/experimental/TranscriptionTask/TranscriptionTask.stories.js @@ -1,123 +1,55 @@ -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 DrawingTask from '../../DrawingTask/components/DrawingTask' -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 / Transcription', + component: DrawingTask, + 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 = { + T3: { + help: 'Underline the line to transcribe with two clicks, then enter in the text transcription.', + instruction: 'Underline and transcribe', + required, + taskKey: 'T3', + tools: [ + { + help: '', + label: 'Draw under the text', + type: 'transcriptionLine' + } + ], + type: 'transcription' + } + } return ( - - - - - + ) } - -storiesOf('Tasks / Transcription', module) - .addDecorator(withKnobs) - .addParameters({ - viewport: { - defaultViewport: 'responsive' - } - }) - .add('light theme', function () { - const tasks = { - T3: { - help: 'Underline the line to transcribe with two clicks, then enter in the text transcription.', - instruction: 'Underline and transcribe', - taskKey: 'T3', - tools: [ - { - help: '', - label: 'Draw under the text', - type: 'transcriptionLine' - } - ], - type: 'transcription' - } - } - const step = { - stepKey: 'S1', - taskKeys: ['T3'] - } - 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 ( - - - - ) - }) \ No newline at end of file diff --git a/packages/lib-classifier/src/store/helpers/createStore/createStore.js b/packages/lib-classifier/src/store/helpers/createStore/createStore.js new file mode 100644 index 0000000000..129bfecf74 --- /dev/null +++ b/packages/lib-classifier/src/store/helpers/createStore/createStore.js @@ -0,0 +1,8 @@ +import asyncStates from '@zooniverse/async-states' +import { types } from 'mobx-state-tree' +import RootStore from '../../RootStore' + +export default function createStore(snapshot = {}) { + const store = RootStore.create(snapshot) + return store +} diff --git a/packages/lib-classifier/src/store/helpers/createStore/index.js b/packages/lib-classifier/src/store/helpers/createStore/index.js new file mode 100644 index 0000000000..976c142eb0 --- /dev/null +++ b/packages/lib-classifier/src/store/helpers/createStore/index.js @@ -0,0 +1 @@ +export { default } from './createStore' diff --git a/packages/lib-classifier/src/store/helpers/index.js b/packages/lib-classifier/src/store/helpers/index.js new file mode 100644 index 0000000000..9976b02470 --- /dev/null +++ b/packages/lib-classifier/src/store/helpers/index.js @@ -0,0 +1 @@ +export { default as createStore } from './createStore' diff --git a/packages/lib-classifier/src/stories/components/MockTask/MockTask.js b/packages/lib-classifier/src/stories/components/MockTask/MockTask.js new file mode 100644 index 0000000000..23396255bb --- /dev/null +++ b/packages/lib-classifier/src/stories/components/MockTask/MockTask.js @@ -0,0 +1,128 @@ +import { Box, Grommet } from 'grommet' +import { Provider, observer } from 'mobx-react' +import React, { useEffect, useState } from 'react' +import { Tasks } from '@components/Classifier/components/TaskArea/components/Tasks/Tasks' +import asyncStates from '@zooniverse/async-states' +import zooTheme from '@zooniverse/grommet-theme' +import { createStore } from '@store/helpers' + +/** + Global store. This should be created only once, otherwise the Provider will error. +*/ +let store +const ObservedTasks = observer(Tasks) + +/** + Takes a workflow tasks object and sets up the active workflow step and classification annotations. +*/ +function addStepToStore(tasks = {}) { + const stepKey = 'S1' + const taskKeys = Object.values(tasks).map(task => task.taskKey) + const step = { + stepKey, + taskKeys + } + const steps = [[stepKey, step]] + store.workflowSteps.setStepsAndTasks({ steps, tasks }) + store.workflowSteps.active.tasks.forEach(task => store.classifications.addAnnotation(task)) +} + +/** + Initialise the store state on story load. +*/ +function initStore(tasks) { + store = store ?? createStore() + addStepToStore(tasks) + const mockSubject = { + id: 'subject', + metadata: {} + } + const mockWorkflow = { + id: 'workflow', + version: '1.0' + } + const mockProject = { + id: 'project' + } + store.classifications.createClassification(mockSubject, mockWorkflow, mockProject) +} + +/** + Scaffolding to display a set of workflow tasks in a story, with a state store. +*/ +export default function MockTask({ + /** Use the dark theme */ + dark = false, + /** subject loading state */ + subjectReadyState = asyncStates.success, + /** a workflow tasks object */ + tasks, + /** any other props to pass down to the tasks */ + ...taskProps +}) { + const [ loaded, setLoaded ] = useState(false) + + useEffect(function init() { + initStore(tasks) + setLoaded(true) + }, []) + + useEffect(function onTasksChange() { + addStepToStore(tasks) + }, [tasks]) + + useEffect(function onSubjectReadyStateChange() { + const { subjectViewer } = store + switch (subjectReadyState) { + case asyncStates.error: { + subjectViewer.onError() + break + } + case asyncStates.loading: { + subjectViewer.resetSubject() + break + } + case asyncStates.success: { + subjectViewer.onSubjectReady() + break + } + } + }, [subjectReadyState]) + + if (!loaded) { + return null + } + + const classification = store?.classifications?.active + const step = store?.workflowSteps?.active + + return ( + + + + + + + + ) +} \ No newline at end of file diff --git a/packages/lib-classifier/src/stories/components/MockTask/index.js b/packages/lib-classifier/src/stories/components/MockTask/index.js new file mode 100644 index 0000000000..48938887fd --- /dev/null +++ b/packages/lib-classifier/src/stories/components/MockTask/index.js @@ -0,0 +1 @@ +export { default } from './MockTask' diff --git a/packages/lib-classifier/src/stories/components/index.js b/packages/lib-classifier/src/stories/components/index.js new file mode 100644 index 0000000000..ece9153a46 --- /dev/null +++ b/packages/lib-classifier/src/stories/components/index.js @@ -0,0 +1 @@ +export { default as MockTask } from './MockTask' diff --git a/packages/lib-classifier/webpack.dev.js b/packages/lib-classifier/webpack.dev.js index c221270951..4711f4f333 100644 --- a/packages/lib-classifier/webpack.dev.js +++ b/packages/lib-classifier/webpack.dev.js @@ -31,9 +31,11 @@ module.exports = { mode: 'development', resolve: { alias: { + '@components': path.resolve(__dirname, 'src/components'), '@helpers': path.resolve(__dirname, 'src/helpers'), '@plugins': path.resolve(__dirname, 'src/plugins'), '@store': path.resolve(__dirname, 'src/store'), + '@stories': path.resolve(__dirname, 'src/stories'), '@test': path.resolve(__dirname, 'test'), '@viewers': path.resolve(__dirname, 'src/components/Classifier/components/SubjectViewer') } diff --git a/packages/lib-classifier/webpack.dist.js b/packages/lib-classifier/webpack.dist.js index 4981f3265f..63a4b88e30 100644 --- a/packages/lib-classifier/webpack.dist.js +++ b/packages/lib-classifier/webpack.dist.js @@ -24,9 +24,12 @@ module.exports = { mode: 'production', resolve: { alias: { + '@components': path.resolve(__dirname, 'src/components'), '@helpers': path.resolve(__dirname, 'src/helpers'), '@plugins': path.resolve(__dirname, 'src/plugins'), '@store': path.resolve(__dirname, 'src/store'), + '@stories': path.resolve(__dirname, 'src/stories'), + '@test': path.resolve(__dirname, 'test'), '@viewers': path.resolve(__dirname, 'src/components/Classifier/components/SubjectViewer') } },