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

Custom Wildcard Field UI #722

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
52d1cf7
Update model, reducers, selectors, and tests
espressoroaster Sep 14, 2017
2bacaf4
Add wildcard-field-editor component
espressoroaster Sep 13, 2017
23df761
Add wildcard-field-dropper component to wrap CustomWildcardField in list
espressoroaster Sep 15, 2017
5ac40cd
Add wildcard-field-drop-zone component
espressoroaster Sep 15, 2017
8a42aa8
Refactor field-list and add CustomWildcardFieldList
espressoroaster Sep 14, 2017
97e8665
Add CustomWildcardFieldList and DropZone to DataPane component
espressoroaster Sep 14, 2017
9f73ca6
Add TODO to remove handle action from Field
espressoroaster Sep 15, 2017
5c4637f
UPDATE THIS COMMIT LATER -- THIS CSS ONLY EXISTS SO VOYAGER WILL COMPILE
espressoroaster Sep 15, 2017
db4a6b1
Add missing dispatch handler to data pane
espressoroaster Sep 15, 2017
fb0e53d
Make small fixes
espressoroaster Sep 15, 2017
fc6e74e
WIP Field / Field-List changes
espressoroaster Sep 15, 2017
efd1071
the right high-level component
kanitw Sep 15, 2017
a531af5
Move onDrop logic out of Field since it's a dumb component
espressoroaster Sep 15, 2017
d638fc1
Add CSS and canDrop monitor
espressoroaster Sep 16, 2017
5da5a02
Remove wildcard field dropper
espressoroaster Sep 16, 2017
30e124f
Add can-drop and is-over CSS for hovering over custom wildcard field
espressoroaster Sep 27, 2017
03c0edc
Remove unnecessary concat []
espressoroaster Sep 29, 2017
b62d6e8
Fix selector import
espressoroaster Feb 11, 2018
deb2c27
Fix custom wildcard field colors if canDrop and onHover
espressoroaster Feb 11, 2018
32dba89
Fix wildcard field drop zone text and css
espressoroaster Feb 11, 2018
8e6dec5
Add title to Custom Wildcard Fields
espressoroaster Feb 11, 2018
3ccc3de
Add Delete Wildcard button to custom wildcard popup
espressoroaster Feb 11, 2018
f22c1ae
Remove remove button from custom wildcard field pills in field list
espressoroaster Feb 11, 2018
16466e6
Fix margin for wildcard field drop zone
espressoroaster Feb 11, 2018
f3b3b6d
Fix isOver + canDrop logic
espressoroaster Feb 11, 2018
8ea9ee7
Add comment to explain type cast
espressoroaster Feb 11, 2018
aef4b0f
Change customWildcardFields to customWildcardFieldDefs during rebase
espressoroaster Mar 9, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
141 changes: 100 additions & 41 deletions src/components/data-pane/field-list.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
import {ExpandedType} from 'compassql/build/src/query/expandedtype';
import {PrimitiveType, Schema} from 'compassql/build/src/schema';
import {isWildcard} from 'compassql/build/src/wildcard';
import * as stringify from 'json-stable-stringify';
import {isWildcard, isWildcardDef} from 'compassql/build/src/wildcard';
import * as React from 'react';
import * as CSSModules from 'react-css-modules';
import {connect} from 'react-redux';
import {OneOfFilter, RangeFilter} from 'vega-lite/build/src/filter';
import {FilterAction} from '../../actions';
import {CUSTOM_WILDCARD_ADD_FIELD, CustomWildcardAction} from '../../actions/custom-wildcard-field';
import {DatasetSchemaChangeFieldType} from '../../actions/dataset';
import {ActionHandler, createDispatchHandler} from '../../actions/redux-action';
import {SPEC_FIELD_AUTO_ADD, SpecFieldAutoAdd} from '../../actions/shelf';
import {FILTER_TOGGLE} from '../../actions/shelf/filter';
import {FieldParentType} from '../../constants';
import {CustomWildcardFieldDef} from '../../models/custom-wildcard-field';
import {State} from '../../models/index';
import {ShelfFieldDef} from '../../models/shelf';
import {createDefaultFilter, filterHasField} from '../../models/shelf/filter';
import {selectPresetWildcardFields, selectSchema, selectSchemaFieldDefs} from '../../selectors';
import {selectCustomWildcardFieldDefs} from '../../selectors/index';
import {selectFilters} from '../../selectors/shelf';
import {Field} from '../field';
import {DroppableField} from '../field/index';
import * as styles from './field-list.scss';
import {TypeChanger} from './type-changer';
import {CustomWildcardFieldEditor} from './wildcard-field-editor';


export interface FieldListProps extends ActionHandler<SpecFieldAutoAdd | DatasetSchemaChangeFieldType | FilterAction> {
export interface FieldListProps extends ActionHandler<
CustomWildcardAction | SpecFieldAutoAdd | DatasetSchemaChangeFieldType | FilterAction> {
fieldDefs: ShelfFieldDef[];
schema: Schema;
filters: Array<RangeFilter | OneOfFilter>;
Expand All @@ -39,20 +44,8 @@ class FieldListBase extends React.PureComponent<FieldListProps, {}> {
}

public render() {
const {fieldDefs, schema} = this.props;
const fieldItems = fieldDefs.map(fieldDef => {
let primitiveType;
if (!isWildcard(fieldDef.field)) {
primitiveType = schema.primitiveType(fieldDef.field);
}
const hideTypeChanger = this.getValidTypes(primitiveType).length < 2;
const key = isWildcard(fieldDef.field) ? stringify(fieldDef) : fieldDef.field;
return (
<div key={key} styleName="field-list-item">
{this.renderComponent(fieldDef, hideTypeChanger, primitiveType)}
</div>
);
});
const {fieldDefs} = this.props;
const fieldItems = fieldDefs.map((fieldDef, index) => this.renderListItem(fieldDef, index));
return (
<div styleName='field-list'>
{fieldItems}
Expand Down Expand Up @@ -87,38 +80,69 @@ class FieldListBase extends React.PureComponent<FieldListProps, {}> {
return createDefaultFilter(fieldDef, domain);
}

private renderComponent(fieldDef: ShelfFieldDef, hideTypeChanger: boolean, primitiveType: PrimitiveType) {
if (hideTypeChanger) {
return this.renderField(fieldDef);
} else {
const popupComponent = this.renderTypeChanger(fieldDef, primitiveType);
return this.renderField(fieldDef, popupComponent);
}
}
private renderListItem(fieldDef: ShelfFieldDef, index: number) {
const {schema, filters, handleAction} = this.props;

let popupComponent;
const isCustomWildcardField = isWildcardDef(fieldDef.field);

private renderTypeChanger(fieldDef: ShelfFieldDef, primitiveType: PrimitiveType) {
const {handleAction} = this.props;
if (!isWildcard(fieldDef.field)) {
return (
<TypeChanger
field={fieldDef.field}
type={fieldDef.type}
validTypes={this.getValidTypes(primitiveType)}
handleAction={handleAction}
/>
);
const primitiveType = schema.primitiveType(fieldDef.field);

if (this.getValidTypes(primitiveType).length < 2) {
popupComponent = this.renderTypeChanger(fieldDef, primitiveType);
}
} else {
if (isCustomWildcardField) {
popupComponent = (
<CustomWildcardFieldEditor
customWildcardFielddef={fieldDef as CustomWildcardFieldDef}
index={index}
handleAction={handleAction}
/>
);
}
}
}

private renderField(fieldDef: ShelfFieldDef, popupComponent?: JSX.Element) {
const {schema, filters} = this.props;
const filter = {
active: !isWildcard(fieldDef.field) && filterHasField(filters, fieldDef.field),
onToggle: this.onFilterToggle
};

return (
<Field
function onDrop(droppedFieldDef: ShelfFieldDef) {
const type = fieldDef.type;

if (droppedFieldDef.field === '*') {
window.alert('Cannot add COUNT');
} else if (type === droppedFieldDef.type) {
let fields: string[];
if (isWildcard(droppedFieldDef.field)) {
if (droppedFieldDef.field === '?') {
fields = schema.fieldNames()
.filter(field => schema.vlType(field) === type);
} else {
fields = droppedFieldDef.field.enum.concat([]);
Copy link
Member

Choose a reason for hiding this comment

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

Why concat empty string?

}
} else {
fields = [droppedFieldDef.field];
}

handleAction({
type: CUSTOM_WILDCARD_ADD_FIELD,
payload: {
fields,
index
}
});
} else {
window.alert('Cannot create a wildcard that mixes multiple types');
}
}

const FieldComponent = isCustomWildcardField ? DroppableField : Field;

const field = (
<FieldComponent
fieldDef={fieldDef}
isPill={true}
draggable={true}
Expand All @@ -128,9 +152,30 @@ class FieldListBase extends React.PureComponent<FieldListProps, {}> {
popupComponent={popupComponent}
onDoubleClick={this.onAdd}
onAdd={this.onAdd}
onDrop={isCustomWildcardField ? onDrop : undefined}
schema={schema}
/>
);

return (
<div key={index} styleName="field-list-item">
{field}
</div>
);
}

private renderTypeChanger(fieldDef: ShelfFieldDef, primitiveType: PrimitiveType) {
const {handleAction} = this.props;
if (!isWildcard(fieldDef.field)) {
return (
<TypeChanger
field={fieldDef.field}
type={fieldDef.type}
validTypes={this.getValidTypes(primitiveType)}
handleAction={handleAction}
/>
);
}
}

private getValidTypes(primitiveType: PrimitiveType): ExpandedType[] {
Expand Down Expand Up @@ -170,8 +215,22 @@ export const PresetWildcardFieldList = connect(
(state: State) => {
return {
fieldDefs: selectPresetWildcardFields(state),
schema: selectSchema(state)
schema: selectSchema(state),
filters: selectFilters(state)
};
},
createDispatchHandler<SpecFieldAutoAdd>()
)(FieldListRenderer);


export const CustomWildcardFieldList = connect(
(state: State) => {
return {
// Somehow TS does not infer type that CustomWildcardFieldDefs can be a ShelfFieldDef
fieldDefs: selectCustomWildcardFieldDefs(state) as ShelfFieldDef[],
Copy link
Member

Choose a reason for hiding this comment

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

There are so too casting in this PR -- I doubt if we really need all of them.

Can you see if upgrading to the latest TS allow you to eliminate a lot of these as?

schema: selectSchema(state),
filters: selectFilters(state)
};
},
createDispatchHandler<SpecFieldAutoAdd | CustomWildcardAction>()
)(FieldListRenderer);
25 changes: 20 additions & 5 deletions src/components/data-pane/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import {Schema} from 'compassql/build/src/schema';
import * as React from 'react';
import * as CSSModules from 'react-css-modules';
import {connect} from 'react-redux';
import {CustomWildcardAction} from '../../actions/custom-wildcard-field';
import {ActionHandler, createDispatchHandler} from '../../actions/redux-action';
import {Dataset, State} from '../../models';
import {VoyagerConfig} from '../../models/config';
import {selectConfig, selectDataset} from '../../selectors/';
import {selectSchema} from '../../selectors/dataset';
import {DataSelector} from '../data-selector';
import * as styles from './data-pane.scss';
import {FieldList, PresetWildcardFieldList} from './field-list';
import {CustomWildcardFieldList, FieldList, PresetWildcardFieldList} from './field-list';
import {CustomWildcardFieldDropZone} from './wildcard-field-drop-zone';

export interface DataPanelProps {
data: Dataset;
export interface DataPanelProps extends ActionHandler<CustomWildcardAction> {
config: VoyagerConfig;
data: Dataset;
schema: Schema;
}

export class DataPaneBase extends React.PureComponent<DataPanelProps, {}> {
public render() {
const {schema, handleAction} = this.props;
const {name} = this.props.data;
const fieldCount = this.props.data.schema.fieldSchemas.length;
const {showDataSourceSelector, manualSpecificationOnly} = this.props.config;
Expand All @@ -29,8 +36,14 @@ export class DataPaneBase extends React.PureComponent<DataPanelProps, {}> {
<div styleName="data-pane-section">
<h3>Wildcard Fields</h3>
<PresetWildcardFieldList/>
<CustomWildcardFieldList/>
<CustomWildcardFieldDropZone
schema={schema}
handleAction={handleAction}
/>
</div>
);

return (
<div className="pane" styleName="data-pane">
<h2 styleName="data-pane-title">Data</h2>
Expand All @@ -54,8 +67,10 @@ export class DataPaneBase extends React.PureComponent<DataPanelProps, {}> {
export const DataPane = connect(
(state: State) => {
return {
config: selectConfig(state),
data: selectDataset(state),
config: selectConfig(state)
schema: selectSchema(state)
};
}
},
createDispatchHandler<CustomWildcardAction>()
)(CSSModules(DataPaneBase, styles));
23 changes: 23 additions & 0 deletions src/components/data-pane/wildcard-field-drop-zone.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.drop-zone {
Copy link
Member

Choose a reason for hiding this comment

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

  1. Instead of declaring a totally new CSS, can this extend some style of Field

  2. The old version of Voyager has nicer CSS here

  • We should rounded corner like normal Field
  • It should be semi-transparent

color: white;
height: 24px;
text-align: center;
padding: 5px;
}

.drop-zone-is-over {
@extend .drop-zone;
border: 1px dashed #4CAF50;
}

.drop-zone-can-drop {
@extend .drop-zone;
font-size: 11px;
border: 1px dashed #8ebdc7;
padding: 0 .5em;
background-color: rgba(187,243,255,.9);
height: 20px;
line-height: 20px;
box-sizing: border-box;
color: rgb(0, 0, 0);
}