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

Add Shortcut to the Dev Classifier #3532

Merged
merged 12 commits into from
Mar 14, 2017

Conversation

wgranger
Copy link
Contributor

@wgranger wgranger commented Mar 1, 2017

Describe your changes.
Adds "shortcut" to the dev classifier and creates tests for the component. Also, some styling was changed to clean up code and use preexisting classes.

Review Checklist

  • Does it work in all major browsers: Firefox, Chrome, Edge, Safari?
  • Does it work on mobile?
  • Can you rm -rf node_modules/ && npm install and app works as expected?
  • Did you deploy a staging branch?
  • Did you get any type checking errors from Babel Typecheck

https://shortcut-to-classifier.pfe-preview.zooniverse.org/

Optional

  • If it's a new component, is it in ES6? Is it clear of warnings from ESLint?
  • Have you replaced any ChangeListener or PromiseRenderer components with code that updates component state?
  • If changes are made to the classifier, does the dev classifier still work?
  • Have you added in flow type annotations?

Copy link
Contributor

@srallen srallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering if the style changes are intended to affect the UI of the button for the whole classifier or if you meant to remove those only when display in the dev classifier. Is there a project in production that is using the shortcut task?

Also could be for a future PR, but the shortcut task is tiny and wouldn't take long to convert to es6.

@wgranger
Copy link
Contributor Author

wgranger commented Mar 3, 2017

This project that is under review uses the shortcut: https://shortcut-to-classifier.pfe-preview.zooniverse.org/projects/sweenkl/steller-watch/classify/?env=production

Styling changes were made because the .unlinked-shortcut class originally styled the shortcuts to always have the dark background of the classifier buttons, as opposed to the light background of the dev classifier buttons. Removing CSS from .unlinked-shortcut and giving shortcuts pre-existing classes solved this.

I can prob write this into ES6 pretty quickly right now.

Copy link
Contributor

@srallen srallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the ES6 rewrite going to come in with this branch or are you gonna submit a separate PR? This PR otherwise looks good to go.

@wgranger
Copy link
Contributor Author

wgranger commented Mar 6, 2017

Oh. Just saw your comment after I pushed. I went ahead and rewrote those two components into ES6 - both the shortcut and the editor attached to it.

Copy link
Contributor

@srallen srallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't really make sense to have the editor in the shared components folder. Could you move it to classifier/tasks?

I'm seeing some prop type warnings when running the tests for Shortcut.

Also, this is unrelated, but could be fixed with this incoming PR. The editor in the workflow edit page in the lab is shown in a if/else statement which if enabled prevents the project owner from editing both the shortcut and their actual task:

screen shot 2017-03-06 at 3 29 17 pm

I should be able to edit my survey task and be able to edit the shortcut task.

It could come in a separate PR, but it'd be good to have tests for the editor too.

};

Summary.defaultProps = {
annotation: null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A default prop of null will throw console warnings if the prop is also required. Either prop can't be required or this needs to be set to a default value.

<div className="unlinked-shortcut">

{options.map((answer, i) => {
if (answer._key == null) { answer._key = Math.random(); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use strict equality whenever possible.

{shortcuts && (
<div className="workflow-task-editor-choices">
{shortcuts.answers.map((shortcut, index) => {
if (shortcut._key == null) { shortcut._key = Math.random(); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use strict equality.

let nextTaskID;

if (e.target.checked) {
const taskCount = Object.keys(this.props.workflow.tasks).length;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uncaught TypeError: Cannot read property 'props' of undefined when trying to use the checkbox button on a test project.

}
const handleChange = handleInputChange.bind(this.props.workflow);
const children = React.Children.map(this.props.children, (child) => {
React.cloneElement(child);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to return here, is why the original task isn't appearing.

Copy link
Contributor

@srallen srallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one issue found with the summary and some comments about mocha/enzyme use.

describe('shortcutRendering', function () {
let wrapper;

beforeEach(function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can just be a before() call. The set of tests in this describe block aren't interacting with the component, just checking the expected render.

let wrapper;

beforeEach(function () {
wrapper = mount(<ShortcutEditor task={task} workflow={workflow} />);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shallow or render is probably a better choice to use here since you're only checking the render and don't need the full React lifecycle available. Once we start getting a lot of the code tested, using mount when not actually needed will slow down the test run. Good explanation of the differences here: enzymejs/enzyme#465 (comment)

};

describe('ShortcutEditor', function () {
describe('shortcutRendering', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be describe('rendering', function() { or whatever makes sense for the block of tests. The describe statements should be readable like the it statements.

});
});

describe('emptyRendering', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about readable describe statement.

let wrapper;

beforeEach(function () {
wrapper = mount(<ShortcutEditor task={emptyTask} workflow={emptyWorkflow} />);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about use of mount.

describe('emptyRendering', function () {
let wrapper;

beforeEach(function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about using before here.

sinon.assert.called(toggleStub);
});

it('should remove the correct shortcut when deleted', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't really testing the removal, but rather that the handler is firing. I think it'll be clearer if described like the previous test it('should call remove handler when clicked') or something like that.

sinon.assert.called(removeStub);
});

it('should add a default shortcut when expanded', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as about about test description.

</div>
);
} else {
answer = <div className="answer">No answer</div>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the dev classifier as well as in a test project, when I click the shortcut option and click done, the summary I see is 'No answer'

} else {
delete (this.props.annotation.shortcut);
this.setState({ index: null });
this.props.classification.update('annotations');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, @eatyourgreens asked a question about the use of the classification resource rather than the onChange handler that other tasks use: #3522 (comment)

@srallen srallen mentioned this pull request Mar 8, 2017
9 tasks
@CKrawczyk
Copy link
Contributor

FYI there have been some changes to the shortcut task to make it work with the new task viewer rewrite: #3571

@wgranger
Copy link
Contributor Author

An issue has been opened on the backend to handle shortcut exports with multiple answers.

Copy link
Contributor

@srallen srallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a few more minor changes and questions for the editor. Also, while testing manually, I've been running into this issue, FYI: zooniverse/panoptes#2212

I can get it to work with a couple of edits, but something we should also let project teams who are going to use this know. cc @trouille

if (this.props.task) {
const answers = this.props.workflow.tasks[this.props.task.unlinkedTask].answers;
answers.splice(index, 1);
if (!answers.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer an explicit comparison here rather than a truthy value, if (answers.length === 0) should work.

{children}
<hr />

<label htmlFor="shortcut" title="Shortcut Options to End Classification">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This htmlFor isn't pointing at an id. You'll need to add id="shortcut" on to the input so the browser knows what this is referencing.

<label htmlFor="shortcut" title="Shortcut Options to End Classification">
<AutoSave resource={this.props.workflow}>
<span className="form-label">Shortcut Option</span>{' '}
<input type="checkbox" checked={shortcuts != null} onChange={this.toggleShortcut} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use strict equality whenever possible in javascript. Loose equality, == and != can have unexpected results because it does type conversion: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness

return (
<div>

{children}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing it, but why does the ShortcutEditor need to wrap the TaskEditorComponent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided to use this Decorator pattern when shortcut was reviewed.


while (!((nextTaskID != null) && !(nextTaskID in this.props.workflow.tasks))) {
taskIDNumber += 1;
nextTaskID = `T${taskCount + taskIDNumber}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 17-23 look to be identical to addNewTask in the workflow lab page: https://github.com/zooniverse/Panoptes-Front-End/blob/master/app/pages/lab/workflow.cjsx#L525-L541

Is there a reason why you're not just passing addNewTask down as a prop and using that? You can rewrite your handler to call addNewTask as well as the couple of Shortcut specific lines, I think just line 29 and line 31?

Copy link
Contributor Author

@wgranger wgranger Mar 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, if addNewTask is bound to the parent (EditWorkflowPage) and passed down as a prop, then the task prop in the shortcut-editor will change when the function is called (since the parent changes the state of selectedTaskKey) and line 29 in shortcut-editor (this.props.task.unlinkedTask = nextTaskID;) will be set on the shortcut task rather than the original task it needs to be linked to. I tried passing the function itself as a prop (addNewTask={this.addNewTask}), but the functionality on the child component was being wonky.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, could you make a note about DRYing this up in the classifier rewrite issue #3527? Let's tackle it again later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

{shortcuts && (
<div className="workflow-task-editor-choices">
{shortcuts.answers.map((shortcut, index) => {
if (shortcut._key == null) { shortcut._key = Math.random(); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use strict equality. See note online 75 about why.

@wgranger
Copy link
Contributor Author

Oh yes. I'm really glad you brought that up. I noticed the issues related to that Panoptes PR. Good it is being mentioned here.

@wgranger wgranger force-pushed the shortcut-to-classifier branch from f65c260 to 7e18a73 Compare March 13, 2017 20:20
Copy link
Contributor

@srallen srallen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and @trouille informed the project using this that the exports aren't fully functional yet.

I think we conceptually need to revisit the shortcut task in general and I'll make a note of it in the rfc issue. I'm not sure how this is now different from using a multiple task type in a combo and setting the multiple task to end the classification.

@srallen srallen merged commit 8e3b896 into zooniverse:master Mar 14, 2017
@wgranger wgranger deleted the shortcut-to-classifier branch April 6, 2017 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants