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

Support field in condition #1980

Merged
merged 16 commits into from
Jun 11, 2017
Merged

Support field in condition #1980

merged 16 commits into from
Jun 11, 2017

Conversation

kanitw
Copy link
Member

@kanitw kanitw commented Feb 24, 2017

Follow up from #1971.

It's probably more natural to do the following way in many cases:

x: {
  condition: { selection: <name>,  field: ..., type: ...},
  value: ...
}

Currently this PR only contains interface changes, but haven't make it work.

This will require a major refactor since we can no longer just refer to
encoding.<channel>.field for accessing field, so I'm gonna punt on it for now.

  • initScale
  • initLegend
  • fieldDef
    • channelHasField
    • getFieldDef
  • aggregate
  • mixins of marks
  • schema rename script

@vega vega deleted a comment from domoritz Jun 8, 2017
- Conditional as a generic type for conditional channelDef
- Condition as a generic type of channelDef with `selection` predicate
@kanitw kanitw force-pushed the kw/condition-field branch 6 times, most recently from 45fcf6b to 4fe69f8 Compare June 9, 2017 06:05
…annelDef.condition)` if applicable

- Introduce
  - `getFieldDef()` which returns the fieldDef -- either from the outer channelDef or from the condition of channelDef
  - `hasConditionFieldDef()` and `isConditionalDef()`
- Correct type signature for subsets of Channel (SingleDefChannel, ScaleChannel, etc.)
- Remove outdated logic for row/column's axis (we no longer use axis for row/column -- and use header instead!)
- Make model.fieldDef() references check for undefined
@kanitw kanitw changed the title [WIP] Support field in condition Support field in condition Jun 9, 2017
@kanitw kanitw requested a review from arvind June 9, 2017 18:36
Copy link
Member

@arvind arvind left a comment

Choose a reason for hiding this comment

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

Hurrah 🎉 for this PR! The changes look good to me. I've left minor questions/comments throughout that may yield some cleaner code paths.

src/fielddef.ts Outdated
*/
export function hasConditionFieldDef<F>(channelDef: ChannelDef<F>): channelDef is (ValueDef<any> & {condition: FieldDef<F>}) {
return !!channelDef && !!channelDef.condition && isFieldDef(channelDef.condition);
}

export function isFieldDef<F>(channelDef: ChannelDef<F>): channelDef is FieldDef<F> | PositionFieldDef<F> | LegendFieldDef<F, any> | OrderFieldDef<F> | TextFieldDef<F> {
return !!channelDef && (!!channelDef['field'] || channelDef['aggregate'] === 'count');
Copy link
Member

Choose a reason for hiding this comment

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

getFieldDef below looks good, but I'm curious why isFieldDef doesn't account for a Conditional field def?

Copy link
Member

Choose a reason for hiding this comment

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

Relatedly, I see that several isFieldDef calls have been replaced with getFieldDef to account for the conditional. If there's a difference in how the two should be used/what they should check for, perhaps we can name them more clearly to reflect that? Unfortunately I don't have suggestions yet because I don't grasp the difference, sorry!

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, say a channelDef could be

  1. FieldDef (with no condition)
  2. FieldDef with ConditionValueDef

(1 & 2 are ConditionalFieldDef as I make condition? so I don't have to declare too many types)

  1. ValueDef (with no condition)
  2. ValueDef with ConditionFieldDef
  3. ValueDef with ConditionValueDef

(3-5 are ConditionalValueDef as I make condition? so I don't have to declare too many types)

  1. ConditionFieldDef only (akin to if without else)
  2. ConditionValueDef only

(6-7 are ConditionOnlyDef)

isFieldDef is more scoped to one object and can check if the object is a fieldDef.
The input can be either:

  • a) the parent object of a channelDef (encoding.<channel>), which will return true for 1 or 2,
  • b) the condition object of a channelDef (encoding.<channel>.condition), which will return true for case 4 or 6.

On the other hand, getFieldDef is for getting the fieldDef from a channelDef.
Given a channelDef (from encoding.<channel>), this will return either the parent channelDef object (for case 1 or 2) or its condition object (for case 4 or 6).

In a way, getFieldDef is more like getFieldDefFromChannelDef but it is kinda verbose, so I just chose the brief getFieldDef. That said, I agree that it can be confusing.

If you have better name suggestions, let me know :)

@@ -1032,32 +1032,47 @@
"$ref": "#/definitions/CompositeUnitSpecAlias",
"description": "Unit spec that can have a composite mark."
},
"Condition<(string|number)>": {
"additionalProperties": false,
"Condition<(LegendFieldDef<Field,number>|ValueDef<number>)>": {
Copy link
Member

Choose a reason for hiding this comment

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

Reminder to update rename-schema for these keys.

@@ -156,10 +156,13 @@ export class UnitModel extends ModelWithField {
if (isFieldDef(channelDef)) {
fieldDef = channelDef;
specifiedScale = channelDef.scale;
} else if (isConditionalDef(channelDef) && isFieldDef(channelDef.condition)) {
fieldDef = channelDef.condition;
specifiedScale = channelDef.condition.scale;
Copy link
Member

Choose a reason for hiding this comment

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

Could these first two branches be consolidated using the new getFieldDef method?

Copy link
Member Author

@kanitw kanitw Jun 9, 2017

Choose a reason for hiding this comment

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

Yeah I tried this earlier too. However, TypeScript won't know that channelDef.condition is not only a FieldDef, but also a ScaleFieldDef. So we either have to do type casting or have to do this and I generally prefer to avoid casting.

_legend[channel] = {...legendSpec};
if (channelDef) {
const legend = isFieldDef(channelDef) ? channelDef.legend :
(channelDef.condition && isFieldDef(channelDef.condition)) ? channelDef.condition.legend : null;
Copy link
Member

Choose a reason for hiding this comment

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

Similarly, here, rather than multiple ternaries depending on where the fieldDef might be, could this be simplified with a single getFieldDef call?

Copy link
Member Author

Choose a reason for hiding this comment

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

Similar to #1980 (comment), TS won't know that this is a LegendFieldDef, not just FieldDef.

src/channel.ts Outdated
@@ -55,6 +55,10 @@ export const TOOLTIP = Channel.TOOLTIP;
export const CHANNELS = [X, Y, X2, Y2, ROW, COLUMN, SIZE, SHAPE, COLOR, ORDER, OPACITY, TEXT, DETAIL, TOOLTIP];
const CHANNEL_INDEX = toSet(CHANNELS);

export const SINGLE_DEF_CHANNELS = [X, Y, X2, Y2, ROW, COLUMN, SIZE, SHAPE, COLOR, OPACITY, TEXT, TOOLTIP];
Copy link
Member

Choose a reason for hiding this comment

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

What is a "single def" channel? Perhaps a comment to explain what this class of channels is and why it's needed?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch.

I'll add

/**
 * Channels cannot have an array of channelDef.
 * model.fieldDef, getFieldDef only work for these channels.
 *
 * (The only two channels that can have an array of channelDefs are "detail" and "order".
 * Since there can be multiple fieldDefs for detail and order, getFieldDef/model.fieldDef
 * are not applicable for them.  Similarly, selection projecttion won't work with "detail" and "order".)
 */

return false;
}

export function grid(model: UnitModel, channel: SpatialScaleChannel, isGridAxis: boolean) {
Copy link
Member

Choose a reason for hiding this comment

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

For my edification, why do we no longer skip grid for ROW and CHANNEL?

Copy link
Member Author

Choose a reason for hiding this comment

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

With the new layout operator, we no longer use axes for row/column but use layout header instead. Here I basically forgot to remove the code.

// Binned fields should capture extents, for a range test against the raw field.
return model.fieldDef(channel).bin ? (bins[p.field] = 1,
// FIXME: Arvind -- please log proper warning when the specified encoding channel has no field
Copy link
Member

Choose a reason for hiding this comment

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

Good catch!

@vega vega deleted a comment from arvind Jun 9, 2017
],
"type": "object"
},
"Condition<(LegendFieldDef|ValueDef<string>)>": {
Copy link
Member Author

Choose a reason for hiding this comment

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

@domoritz looks like we have a bug in the schema generator?

Some properties are missing here

Copy link
Member Author

Choose a reason for hiding this comment

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

(None of the properties from LegendFieldDef or ValueDef are here at all.)

@kanitw
Copy link
Member Author

kanitw commented Jun 11, 2017

@domoritz

Upgrade to https://github.com/vega/typescript-to-json-schema/releases/tag/v0.4.0

I got Error: Cannot find module '../dist/typescript-to-json-schema' after upgrading

@kanitw
Copy link
Member Author

kanitw commented Jun 11, 2017

@arvind -- I added a few more commits to simplify the schema.
Now we longer have generics in the schema. I also re-declare the part that triggers a bug in the schema generator with a better way to define it and also avoid the bug.

@arvind
Copy link
Member

arvind commented Jun 11, 2017

Travis is detecting differences with the schema. Is that due to the issue you mention above?

@kanitw
Copy link
Member Author

kanitw commented Jun 11, 2017

@arvind I don't think so. I just force push an update. looks like I somehow forgot to commit the diff.

@kanitw
Copy link
Member Author

kanitw commented Jun 11, 2017

Note that we can update the schema and remove b82b8ab in a separate PR.
So if this passes and you like everything else, we can merge.

@arvind arvind merged commit b9b2830 into master Jun 11, 2017
@arvind arvind deleted the kw/condition-field branch June 11, 2017 04:37
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.

None yet

3 participants