-
Notifications
You must be signed in to change notification settings - Fork 58
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
[FEATURE] Ability to edit thresholds in panel options for gauge chart #968
Conversation
Signed-off-by: Eunice Wong <eunicewong@live.com>
Signed-off-by: Eunice Wong <eunicewong@live.com>
7edd74c
to
fe05375
Compare
@@ -43,6 +43,7 @@ | |||
"lodash-es": "^4.17.21", | |||
"mathjs": "^10.6.4", | |||
"mdi-material-ui": "^7.4.0", | |||
"react-colorful": "^5.6.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.
I was choosing between either react-color or react-colorful, the two most popular color picker libraries.
I ended up choosing react-colorful
because the design is simple (@foxbat07 approved), package is smaller, and it is built with react hooks and functional components. It also ended up being very easy to use. But definitely feel free to let me know if you think otherwise/have a strong opinion which color picker to use!
export type GaugeColorStop = [number, string]; | ||
|
||
export type EChartsAxisLineColors = GaugeColorStop[]; | ||
|
||
export interface StepOptions { |
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.
moved StepOptions
and ThresholdOptions
to plugin-system since I needed these types in ThresholdsEditor
which lives in plugin-system/components
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.
Another option could be to move ThresholdsEditor to @perses-dev/components and the types to @perses-dev/core.
We definitely need to decide on long term rules for what goes in each package since currently it isn't consistent (see discussion here: LegendOptionsEditor, UnitSelector, unit types are in @perses-dev/components for instance). Probably fine to wait until @juliepagano looks into package reorganization though too
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 think ThresholdsEditor being in @perses-dev/components will also make it easier to have context-aware components in the future (ex: TimeSeriesChart proposal). If it lives in plugin-system, then ColorPicker.tsx would need to move to avoid a circular dependency for instance
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 think i was debating to move it to @perses-dev/components but got confused since some components like CalculationSelector are in plugin-system. Can't wait to get this figure out lol.
Anyways moved ThresholdsEditor to @perses-dev/component 😄
const defaultThresholdSteps: EChartsAxisLineColors = [[0, defaultThresholdColor]]; | ||
|
||
if (thresholds.steps !== undefined) { | ||
// https://echarts.apache.org/en/option.html#series-gauge.axisLine.lineStyle.color | ||
// color segments must be decimal between 0 and 1 | ||
const segmentMax = 1; | ||
const valuesArr: number[] = thresholds.steps.map((step: StepOptions) => { | ||
if (thresholds.mode === 'percentage') { | ||
return step.value / 100; |
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.
we will have to update this once we support min
Signed-off-by: Eunice Wong <eunicewong@live.com>
Signed-off-by: Eunice Wong <eunicewong@live.com>
a7726dd
to
11a66ed
Compare
Signed-off-by: Eunice Wong <eunicewong@live.com>
Signed-off-by: Eunice Wong <eunicewong@live.com>
49f101f
to
b6745e4
Compare
Signed-off-by: Eunice Wong <eunicewong@live.com>
RED: 'rgba(234, 71, 71, 1)', // red.500 | ||
}; | ||
|
||
export const ThresholdColorsPalette = [ThresholdColors.ORANGE, ThresholdColors.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.
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.
talked to @foxbat07 about this and decided to follow the mui alert colors (info, warning, error)
thresholds: {
defaultColor: muiTheme.palette.success.main,
palette: [muiTheme.palette.info.main, muiTheme.palette.warning.main, muiTheme.palette.error.main],
}
Signed-off-by: Eunice Wong <eunicewong@live.com>
Signed-off-by: Eunice Wong <eunicewong@live.com>
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.
This is so nice, yay for our users
const inputValue = e.target.value.replace(/([^0-9A-F]+)/gi, '').substring(0, 8); | ||
setValue(`#${inputValue}`); | ||
// only set color if input value is a valid hex color | ||
if (isValidHex(e.target.value)) { |
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 see there's a difference in usage between color
and value
-- if you wanted, you could leave a comment above the useState
lines to explain the difference between the two variables
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'm reading as:
color
stores the actual input from the user, but only if it's valid hex format
value
stores the hex value we want to use, including the #
, and does some sanitizing of the user input -- is that right?
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.
Oh maybe it's that value
is meant to be the visible value for the controlled text input, and color
is what we'll pass around internally to use in other parts of the code -- ok I think I'm following now
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're correct - i'll add comments in the code to make this more clear :)
); | ||
}; | ||
|
||
const isValidHex = (value: string, alpha?: boolean): boolean => { |
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.
Does the color-picker library come with a function that does this?
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.
not yet but i actually got this function from the library. It currently only exports an input component with the validation logic, but there is an open issue to support custom input components omgovich/react-colorful#157
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.
sweet, sg
fullWidth | ||
value={value} | ||
onChange={handleInputChange} | ||
/> |
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.
food for thought: when I'm entering colors, I sometimes don't know if I'm supposed to use rgb
format (e.g. (24, 32, 150)) or hex
format.
ideas:
- We could add a label that says "Hex value" or something like
- We could have a placeholder value in hex format
not a big deal though, just ideas
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.
right now it always prefixes the input with a pound sign to indicate that it's hex format and i don't think it should let you enter parentheses and commas
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.
oo yes the #
prefix does exactly that!
<Stack flex={1} direction="row" alignItems="center" justifyContent="space-between" spacing={1}> | ||
<ThresholdColorPicker label={label} color={color} onColorChange={onColorChange} /> | ||
<FormLabel htmlFor={label}>{label}</FormLabel> | ||
<TextField id={label} inputRef={inputRef} type="number" value={value} onChange={onChange} onBlur={onBlur} /> |
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.
Yessss love using the type="number"
(those little up and down arrows!)
iconColor?: string; | ||
isSelected?: boolean; | ||
}>(({ iconColor, isSelected }) => ({ | ||
backgroundColor: isSelected && iconColor ? `${iconColor}3F` : 'undefined', // 3F represents 25% opacity |
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.
Pretttty
const [steps, setSteps] = useState(thresholds?.steps); | ||
useEffect(() => { | ||
setSteps(thresholds?.steps); | ||
}, [thresholds?.steps]); |
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.
Noticed that we're using palette
in other places (vs steps
). Is palette
like a simplified version of the same data in steps?
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.
palette
is the color palette we'll use in the chart and steps
are the actual values.
for example, if steps[0] is 10 and palette[0] is yellow, we'll show yellow for values greater than or equal to 10
if (!recentlyAddedInputRef.current || !focusRef.current) return; | ||
recentlyAddedInputRef.current?.focus(); | ||
focusRef.current = false; | ||
}, [steps?.length]); |
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.
Might not be possible, but is there a way to call .focus()
from inside the addThresholdChange
handler and get it to work?
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.
Hmm i don't think so? We don't have a ref to the text input yet since it hasn't been rendered until steps
is updated
const color = palette[steps.length] ?? getRandomColor(); // we will assign color from the palette first, then generate random color | ||
steps.push({ color, value: (lastStep?.value ?? 0) + 10 }); | ||
} else if (steps) { | ||
steps.push({ value: 10 }); |
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.
Is this saying that any newly added threshold defaults to a value of 10
?
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.
if there is no previous threshold set (steps.length === 0
), then the first threshold you add will default to 10
Signed-off-by: Eunice Wong <eunicewong@live.com>
UX feedback for a potential follow-up with @foxbat07. I find the top bar that communicates the ranges a little overwhelming to process quickly in some cases (e.g. it's a lot when there's a bunch of gauges right next to each other) because there's so many colors. I wonder if it might be helpful to add an option where you can disable them if you like? |
onChange(updatedThresholds); | ||
} | ||
}; | ||
|
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.
Optional suggestion -- You've got a decent amount of logic around adjusting these values that's probably a bit of a pain to test in the context of a react component. If you pulled the core logic out into a few utility functions that you call from the component, you could more easily write some unit tests on those functions outside the react context. That said, you've already written a bunch of tests for the components, so may not be worth refactoring now.
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.
oh nice i'll keep that in mind for next time, and writing those tests in react context actually helped me uncover a few accessibility issues
If you have the time, I think this would be a good case to add some additional visual tests for the gauge chart. Let me know if you want help doing so. |
Signed-off-by: Eunice Wong <eunicewong@live.com>
yes definitely! i am hoping to do this as one of the follow ups since this PR is already so big |
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.
LGTM! 🚀
|
||
interface ColorPickerProps { | ||
initialColor?: string; | ||
onColorChange?: (color: string) => void; |
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.
What is the purpose of onColorChange
? Seems like initialColor
means that component is uncontrolled so I wonder if this can be misleading. Otherwise, it might make sense to use the controlled component paradigm with value
and onChange
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 call, replaced initialColor
with color
and onColorChange
to onChange
Signed-off-by: Eunice Wong <eunicewong@live.com>
348c92e
to
d20a41a
Compare
Signed-off-by: Eunice Wong <eunicewong@live.com>
…hreshold-gauge Signed-off-by: Eunice Wong <eunicewong@live.com>
Signed-off-by: Eunice Wong <eunicewong@live.com>
Signed-off-by: Eunice Wong <eunicewong@live.com>
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.
🚀
CHANGES
percentage
modeTO DO
min
?