-
Notifications
You must be signed in to change notification settings - Fork 0
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 getColorScale to enable displaying multiple lines in LineChart #11
Conversation
fill={color} | ||
/> | ||
)); | ||
const graphGroup = dataGroup.map( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After adding color encoding the render function is larger and larger (more than 100~ lines now!). I'm not sure if there are better way to arrange them. Comment / idea are welcome =D.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm...it is indeed. But I cannot think of a way to deal with this neither. Maybe the <DataLine>
could be extracted.
const fieldsToGroupBy: string[] = [color] | ||
.filter(encoding => !!encoding) | ||
.map(encoding => encoding.field); | ||
const sortedData = data.sort((rowA, rowB) => xSelector.getOriginalVal(rowA) - xSelector.getOriginalVal(rowB)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found that if we don't sort it the lines will not be a path always from left to right..
@@ -88,6 +93,10 @@ export interface Theme { | |||
colors: { | |||
/** colors used for nominal data */ | |||
category: ReadonlyArray<string>; | |||
sequential: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks wonderful. Nice handlings with the colors scales and the data grouping functions 🎉✨
Some documentation may be further included.
@@ -29,7 +29,7 @@ export interface Scale { | |||
/** | |||
* Range of input channel | |||
*/ | |||
range: [any, any]; | |||
range?: ReadonlyArray<any>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice use of ReadonlyArray
👍
|
||
import { getRecordFieldSelector } from './getRecordFieldSelector'; | ||
|
||
interface GetColorScaleArgs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could it be named as ColorScaleArgs
? Get
as the initials sound like a function (though it is capitalized).
}; | ||
}, | ||
{}, | ||
)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about using the map
function of lodash
?
const domain = _.map(data, 'field');
}; | ||
} | ||
default: { | ||
throw Error('Invalid color encoding type.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice error message 👍
case 'quantitative': { | ||
const values = data.map(row => Number(row[field])); | ||
const min = Math.min(...values); | ||
const max = Math.max(...values); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about utilizing the d3-array to handle calculations of the range?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea :)
* [{ type: 'a', color: 'green' }], | ||
* [{ type: 'b', color: 'green' }], | ||
* [{ type: 'a', color: 'red' }], | ||
* [{ type: 'b', color: 'red' }], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice documentation 👍
I was wondering if this example could be extended with another field (such as value: 1
) so that the example could be easier to be understood at first glance.
* [{ type: 'b', color: 'red' }], | ||
* ] | ||
*/ | ||
export function getDataGroup(data: object[], fields: string[]): object[][] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be renamed as getGroupedDataByFields
?
const defaultColor = theme.colors.category[0]; | ||
const getColor = colorScale ? | ||
colorScale.selector.getScaledVal : | ||
() => defaultColor; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may follow the coding style of ternaries from airbnb:
const getColor = colorScale
? colorScale.selector.getScaledVal
: () => defaultColor;
() => defaultColor; | ||
const fieldsToGroupBy: string[] = [color] | ||
.filter(encoding => !!encoding) | ||
.map(encoding => encoding!.field); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this could be extracted as a function in utils
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for suggestion! I would extract a getDataGroupByEncodings
since the fieldsToGroupBy
is only used in building dataGroup
.
fill={color} | ||
/> | ||
)); | ||
const graphGroup = dataGroup.map( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm...it is indeed. But I cannot think of a way to deal with this neither. Maybe the <DataLine>
could be extracted.
1102a4b
to
705a2af
Compare
1bc4867
to
5f7f4e5
Compare
@garfieldduck Thanks for review! I have fix them and you can review again now :-) |
data: object[]; | ||
} | ||
|
||
function getNumericDomain(values: number[]): [number, number] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
d3Extent
return [number, number] | [undefined | undefined]
so I have to filter out the undefined result in getNumericDomain
@@ -69,10 +76,43 @@ const HoveringIndicator: FunctionComponent<{ | |||
); | |||
}; | |||
|
|||
const DataLine: React.FC<{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe I saw this usage instead in #12? I'm not sure which one is correct though.
const DataLine: React.FC<{ | |
const DataLine: React.FunctionComponent<{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The source code of react type definition:
type SFC<P = {}> = FunctionComponent<P>;
/**
* @deprecated as of recent React versions, function components can no
* longer be considered 'stateless'. Please use `FunctionComponent` instead.
*
* @see [React Hooks](https://reactjs.org/docs/hooks-intro.html)
*/
type StatelessComponent<P = {}> = FunctionComponent<P>;
type FC<P = {}> = FunctionComponent<P>;
interface FunctionComponent<P = {}> {
(props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
So the FC
and FunctionComponent
should be the same thing. But indeed we had FunctionComponent
in previous PR so I will make it consistent with that.
Also I read an article said that you shouldn't use FunctionComponent
to type your function al component because they
-
define additional props like
children
andcontextType
, which may not be supported by your component. -
break
defaultProps
resolution in typescript 3.1 (I'm stilly not fully understanding what that means in the article)
For now, I'm not sure if it's really that bad to have FunctionComponent
type so I would just keep FunctionComponent
here. Once it's clear that we should not use FC
or FunctionComponent
I may make a fix for this.
/** Width of the collision detection rectangle */ | ||
const color = theme.colors.category[0]; | ||
const bandWidth = graphWidth / (data.length - 1); | ||
const colorScale = typeof color !== 'undefined' && getColorScale({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll suggest to wrap this expression to make it more prominent.
const colorScale = typeof color !== 'undefined' && getColorScale({ | |
const colorScale = (typeof color !== 'undefined') && getColorScale({ |
return [ | ||
typeof extent[0] === 'undefined' ? 0 : extent[0], | ||
typeof extent[1] === 'undefined' ? 0 : extent[1], | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some syntax sugar:
const [extentMin = 0, extentMax = 0] = d3.extent(values);
return [extentMin, extentMax];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks nice to me in general
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! Looks fantastic to me 💯 ✨
fill={color} | ||
/> | ||
)); | ||
return (<> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<>
may be moved into a new line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah nice catch
+1 with this
Purpose
Add
getColorScale
. Leverage this color scale to paint multiple lines in LineChart.Change
getColorScale
in graph package.color
encoding prop)UI screenshot
Risk
None.
TODO
[ ] Handle quantitative scaling properly so the lightest line in example in "quantitative field as color" could be clearer.