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

Comparison Filter GUI #64

Merged
merged 16 commits into from May 18, 2018
Merged

Conversation

chrismayer
Copy link
Collaborator

@chrismayer chrismayer commented May 16, 2018

This introduces a GUI visualizing a ComparisonFilter. The GUI consists of a bunch of react components.

For demonstration purpose a little demo app has also been developed, where the following can be done:

  • Select a GeoJSON file and import it into the app
  • Select an attribute, an operator and a value to define the ComparisonFilter
  • Dependent on the attribute type (String, Number, Boolean) a different UI is shown
  • The internal ComparisonFilter object can be serialized and is printed into a text field

image

UI for boolean attributes:
image

This adds several components, which can be used to aggregate to a
Comparison Filter UI.
For demonstration purposes a basic Demo UI is also implemented.
(Addresses #58)
This adds several components, which can be used to aggregate to a
Comparison Filter UI.
For demonstration purposes a basic Demo UI is also implemented.
(Addresses #58)
Adds an UploadButton component which let the user select a geodata file
as base for the filter UI.
This enables the ComparisonFilterUi to hold a GeoStyler ComparisonFilter
object derived from its GUI elements.
This adds a text area to output the serialized GeoStyler filter in the
FilterDemoUi.
Disable button to add more ComparisonFilterUis since we cannot handle
multiple filters at the moment.
*/
class AttributeCombo extends React.Component<any, any> {

// PROPS
Copy link
Collaborator

Choose a reason for hiding this comment

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

React usually defines its props with PropTypes.
As we make use of TypeScript another approach with interfaces should be the way to go.

The props should not be declared as member variables. The declared member variables will not be associated with the props. So you will have this.internalDataDef and this.props.internalDataDef and so on.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point, I have this on my list. This is the next thing I will tackle.


</div>
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

You might want to profit from object destructuring / spread operator for the props. You can then use "passThorughProps".
Compare

@@ -0,0 +1,4 @@

Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use less instead of css

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I created a separate issue for this (#65), so we can tackle this later (since this seems a bit more complicated that one might think at first sight).

* - A combo to select the operator
* - An input field for the value
*/
class ComparisonFilterUi extends React.Component<any, any> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove "Ui" from the classname

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK...

*/
class ComparisonFilterUi extends React.Component<any, any> {

// PROPS
Copy link
Collaborator

Choose a reason for hiding this comment

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

See comment above regarding props.

*
* Changes the input field for the filter value and stores the appropriate attribute name as member.
*/
onAttributeChange = (newAttrName: string) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please transform methods to following syntax and use .bind(this) at the call if needed:

onAttributeChange(newAttrName: string) {

Otherwise styleguidist doesn't recognize this methods as methods but as properties.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I will check that

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

At the moment binds are forbidden in JSX attributes due to their rendering performance impact (tslint).
So what do you prefer? Making styleguidist happy or relying on the tslint recommendation not to use bind?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I wasn't aware that TypeScript recommends this way of declaring class methods. So
we should stick to the TypeScript recommendation for know.

@@ -0,0 +1,77 @@
import * as React from 'react';
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure if it makes a difference but i'd prefer import React from 'react';.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Does not work for me. The module doesn't seem to have a standard export. And using import { React } from 'react'; also does not work because there is no exported member React in the module.
I also just discovered import * as React from 'react'; in all documentations / tutorials I read so far.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It should work if you leave the curly-brackets. Never mind. It's just Schmuck at the Nachthemd...

/** Handler function bound to the 'onChange' of the underlying Select */
onAttributeChange: Function;

constructor(props: any) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you don't perform any actrions with props or the state you can omit the constructor.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agree...

@KaiVolland
Copy link
Collaborator

Looks very promising. 😍 I added some notes to structural things.
Would be cool if you could add a screenshot once your happy with the visualization.

@chrismayer
Copy link
Collaborator Author

Thanks for your review @KaiVolland! Good points, I agree with most of them. I will go on and address your remarks / hints.

This reworks the filter UI components so the react properties are
modelled corretly for TypeScript.
Concept taken from:
https://charleslbryant.gitbooks.io/hello-react-and-typescript/content/TypeScriptAndReact.html
Detects the min and max value from the attribute schema for numeric
attributes.
Adds a BoolFilterField component offering a checkbox to set the filter
value for attributes of type boolean .
@chrismayer chrismayer changed the title [WIP] Comparison Filter GUI Comparison Filter GUI May 17, 2018
@chrismayer
Copy link
Collaborator Author

I addressed or at least commented your remarks, @KaiVolland. I think this is now ready for another (and hopefully final) review.

Copy link
Collaborator

@KaiVolland KaiVolland left a comment

Choose a reason for hiding this comment

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

Very nice. Feel free to merge.

Could you explain in one or two sentences (or maybe a source) why you decide to split the props interface in "default props" and "non default props"?

@@ -0,0 +1,77 @@
import * as React from 'react';
Copy link
Collaborator

Choose a reason for hiding this comment

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

It should work if you leave the curly-brackets. Never mind. It's just Schmuck at the Nachthemd...

operator: ComparisonOperator;

/** The currently entered filter value */
value: string | number | boolean | null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe the 4 props above should go to the state, too. But we can adjust this later.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think so. Since setting the state is an asynchronous action (by triggering the rendering not immediately) this can lead to divergence in the values for attribute, operator and value, when serializing them. Holding them as "normal" members ensures the correct values at every time.
For my understanding the state should be used for information, which is directly connected to a component.

@chrismayer chrismayer merged commit e531962 into geostyler:master May 18, 2018
@chrismayer
Copy link
Collaborator Author

Thanks again for your review @KaiVolland!

Could you explain in one or two sentences (or maybe a source) why you decide to split the props interface in "default props" and "non default props"?

The "default props" are these props, which are optional and have a default value. The other have to be passed in by the father component. By modeling the "default props" in an own interface we can define a static defaultProps element in our component class to set the default values.

  public static defaultProps: DefaultAttributeComboProps = {
    label: 'Attribute',
    placeholder: 'Select Attribute',
  };

I found several sources describing this, e.g.:

https://stackoverflow.com/questions/37282159/default-property-value-in-react-component-using-typescript/37282264

@chrismayer chrismayer mentioned this pull request May 18, 2018
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

2 participants