-
-
Notifications
You must be signed in to change notification settings - Fork 0
Jan26 qa fixes #277
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
Jan26 qa fixes #277
Conversation
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.
Pull request overview
This PR contains QA fixes for the visualization panel, focusing on UI improvements and stepped color editor logic refinements for handling data source and column changes.
Changes:
- Updated grid layouts and select components with better minimum width constraints to prevent overflow issues
- Added comprehensive logic to reset and validate stepped color steps when data sources or columns change
- Adjusted layer header padding for consistent spacing
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| VisualisationPanel.tsx | Updated grid column sizing from minmax(0,1fr) to minmax(200px,1fr), added min-w-0 to SelectTrigger components, and replaced fragment with div wrapper for proper grid spanning |
| SteppedColorEditor.tsx | Added useEffect hooks to track and reset stepped color steps on data source/column changes, introduced strict validation logic for step ranges, and refactored local step initialization |
| LayerHeader.tsx | Changed padding from pt-3 to py-3 for balanced vertical spacing |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| areaStats?.primary && | ||
| minValue !== undefined && | ||
| maxValue !== undefined && | ||
| maxValue >= minValue && |
Copilot
AI
Jan 20, 2026
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 condition maxValue > minValue is redundant because it's already checked by maxValue >= minValue on line 188. Additionally, dividing by zero isn't possible when these values are equal, so line 189 should be the only check needed. Consider removing line 188 or using only maxValue > minValue to avoid redundant conditions.
| maxValue >= minValue && |
| colorScheme, | ||
| isReversed, | ||
| viewConfig.customColor, |
Copilot
AI
Jan 20, 2026
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.
Including colorScheme, isReversed, and viewConfig.customColor in the dependency array will cause this effect to run whenever the user changes the color scheme. However, this effect is intended to reset steps only when the column or data source changes (as indicated by the comment on line 171). This could lead to unexpected behavior where changing the color scheme resets the user's custom step ranges. Consider removing these color-related dependencies if the intent is to only reset on column/data source changes.
| colorScheme, | |
| isReversed, | |
| viewConfig.customColor, |
| // Simplified: if steps don't match current data range, always use defaults | ||
| const stepRanges = useMemo(() => { | ||
| if (minValue === 0 && maxValue === 0) { | ||
| if (!minValue && !maxValue) { |
Copilot
AI
Jan 20, 2026
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 condition change introduces a bug. The original condition checked if both values equal 0, but the new condition using !minValue && !maxValue will also return true when minValue is 0 and maxValue is 0, which is a valid data range (a constant value of 0). This will incorrectly return an empty array for valid data. The condition should explicitly check for undefined/null or use the original equality check.
|
|
||
| // Default: 3 steps evenly distributed (fall through if no steps or invalid) | ||
|
|
||
| // Default: 3 steps evenly distributed |
Copilot
AI
Jan 20, 2026
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.
Duplicate comment. There are two identical comments "Default: 3 steps evenly distributed" on consecutive lines. Remove one of these duplicate comments.
| // Default: 3 steps evenly distributed |
| // Clear invalid steps and set defaults when data range changes | ||
| useEffect(() => { | ||
| if (!viewConfig.steppedColorSteps?.length && steps) { | ||
| if (!steps || steps.length === 0 || (!minValue && !maxValue)) { |
Copilot
AI
Jan 20, 2026
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 condition has the same bug as line 238. When minValue and maxValue are both 0 (a valid constant value), !minValue && !maxValue evaluates to true, causing the function to return early incorrectly. This should check for undefined/null values instead.
| if (!steps || steps.length === 0 || (!minValue && !maxValue)) { | |
| if (!steps || steps.length === 0 || minValue == null || maxValue == null) { |
| const firstStep = configSteps[0]; | ||
| const lastStep = configSteps[configSteps.length - 1]; | ||
|
|
||
| // Strict check: ALL steps must be within [minValue, maxValue] | ||
| const allStepsInRange = configSteps.every( | ||
| (step) => | ||
| step.start >= minValue && | ||
| step.end <= maxValue && | ||
| step.start <= step.end, | ||
| ); | ||
|
|
||
| const boundariesMatch = | ||
| firstStep && | ||
| lastStep && | ||
| Math.abs(firstStep.start - minValue) <= tolerance && | ||
| Math.abs(lastStep.end - maxValue) <= tolerance; | ||
|
|
Copilot
AI
Jan 20, 2026
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 validation logic is duplicated from the stepRanges useMemo (lines 245-263). The exact same validation (checking allStepsInRange and boundariesMatch with the same tolerance and logic) appears in both places. Consider extracting this into a separate function to avoid duplication and improve maintainability.
| const firstStep = configSteps[0]; | |
| const lastStep = configSteps[configSteps.length - 1]; | |
| // Strict check: ALL steps must be within [minValue, maxValue] | |
| const allStepsInRange = configSteps.every( | |
| (step) => | |
| step.start >= minValue && | |
| step.end <= maxValue && | |
| step.start <= step.end, | |
| ); | |
| const boundariesMatch = | |
| firstStep && | |
| lastStep && | |
| Math.abs(firstStep.start - minValue) <= tolerance && | |
| Math.abs(lastStep.end - maxValue) <= tolerance; | |
| function validateSteppedColorConfig( | |
| stepsToValidate: SteppedColorStep[], | |
| min: number, | |
| max: number, | |
| tol: number, | |
| ): { allStepsInRange: boolean; boundariesMatch: boolean } { | |
| const first = stepsToValidate[0]; | |
| const last = stepsToValidate[stepsToValidate.length - 1]; | |
| // Strict check: ALL steps must be within [min, max] | |
| const allStepsInRange = stepsToValidate.every( | |
| (step) => | |
| step.start >= min && | |
| step.end <= max && | |
| step.start <= step.end, | |
| ); | |
| const boundariesMatch = | |
| !!first && | |
| !!last && | |
| Math.abs(first.start - min) <= tol && | |
| Math.abs(last.end - max) <= tol; | |
| return { allStepsInRange, boundariesMatch }; | |
| } | |
| const { allStepsInRange, boundariesMatch } = validateSteppedColorConfig( | |
| configSteps, | |
| minValue, | |
| maxValue, | |
| tolerance, | |
| ); |
| const stepSize = (maxValue - minValue) / 3; | ||
| const defaultStepRanges = [ | ||
| { start: minValue, end: minValue + stepSize }, | ||
| { start: minValue + stepSize, end: minValue + stepSize * 2 }, | ||
| { start: minValue + stepSize * 2, end: maxValue }, | ||
| ]; | ||
|
|
||
| // Calculate colors for default steps | ||
| const interpolator = getInterpolator(colorScheme, viewConfig.customColor); | ||
| const defaultSteps = defaultStepRanges.map((rangeItem, index) => { | ||
| const numSteps = defaultStepRanges.length; | ||
| const gradientPosition = numSteps > 1 ? index / (numSteps - 1) : 0; | ||
| const t = isReversed ? 1 - gradientPosition : gradientPosition; | ||
| const clampedT = Math.max(0, Math.min(1, t)); | ||
| const color = interpolator(clampedT); | ||
|
|
||
| return { | ||
| start: rangeItem.start, | ||
| end: rangeItem.end, | ||
| color: color || "#cccccc", | ||
| }; | ||
| }); |
Copilot
AI
Jan 20, 2026
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 default step calculation logic is duplicated in multiple places in this file (lines 278-282 for ranges, lines 287-304 for steps with colors). The same calculation pattern appears three times. Consider extracting this into a shared helper function to reduce duplication and improve maintainability.
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.
Pull request overview
Copilot reviewed 17 out of 18 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| defaultSteps, | ||
| savedSteppedColorSteps?.length, | ||
| updateViewConfig, | ||
| viewConfig.steppedColorStepsByKey, |
Copilot
AI
Jan 21, 2026
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 useEffect has defaultSteps in its dependency array, but defaultSteps is a memoized object that will create a new reference whenever hasValidRange, maxValue, or minValue changes. This could cause the effect to run more frequently than intended. Additionally, the effect calls updateViewConfig which is likely stable, but including the entire viewConfig.steppedColorStepsByKey object in dependencies could cause unnecessary re-renders. Consider using a more specific dependency or restructuring to avoid infinite loops.
| defaultSteps, | |
| savedSteppedColorSteps?.length, | |
| updateViewConfig, | |
| viewConfig.steppedColorStepsByKey, | |
| hasValidRange, | |
| minValue, | |
| maxValue, | |
| savedSteppedColorSteps?.length, | |
| updateViewConfig, |
src/app/map/[id]/components/controls/VisualisationPanel/SteppedColorEditor.tsx
Outdated
Show resolved
Hide resolved
| const [localSteps, setLocalSteps] = useState<SteppedColorStep[]>( | ||
| savedSteppedColorSteps || [], | ||
| ); | ||
|
|
Copilot
AI
Jan 21, 2026
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.
When the dialog is opened, localSteps is not synchronized with the current savedSteppedColorSteps. The state is only initialized once on mount (line 198-200). If savedSteppedColorSteps changes after the component mounts (e.g., from another source or due to data loading), the dialog will display stale data. Consider adding a useEffect that updates localSteps when savedSteppedColorSteps changes or when the dialog opens.
| // Keep localSteps in sync with saved steps or defaults when the dialog opens | |
| useEffect(() => { | |
| if (!isOpen) { | |
| return; | |
| } | |
| if (savedSteppedColorSteps && savedSteppedColorSteps.length > 0) { | |
| setLocalSteps(savedSteppedColorSteps); | |
| } else if (hasValidRange) { | |
| setLocalSteps(defaultSteps); | |
| } | |
| }, [isOpen, savedSteppedColorSteps, defaultSteps, hasValidRange]); |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…dColorEditor.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Description
Motivation and Context
How Can It Be Tested?
How Will This Be Deployed?
Screenshots (if appropriate):
Types of changes
Checklist: