From 04dfd31b1c0ef738af254f3ca8a0b399919930ef Mon Sep 17 00:00:00 2001 From: Renoir Boulanger Date: Mon, 16 Mar 2026 13:14:36 -0400 Subject: [PATCH 01/13] CLIM-1291: BEGIN From 4c2c388e79fda2eebb1957e56d5f6a5f2c95a08e Mon Sep 17 00:00:00 2001 From: Renoir Boulanger Date: Mon, 16 Mar 2026 13:15:07 -0400 Subject: [PATCH 02/13] CLIM-1291: Disable checkbox when not Forecast --- apps/src/components/fields/skill.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/src/components/fields/skill.tsx b/apps/src/components/fields/skill.tsx index 99eee296b..38c0115a2 100644 --- a/apps/src/components/fields/skill.tsx +++ b/apps/src/components/fields/skill.tsx @@ -8,6 +8,8 @@ import { __ } from '@/context/locale-provider'; import TooltipWidget from '@/components/ui/tooltip-widget'; import React from 'react'; +import { ForecastDisplays } from '@/types/climate-variable-interface'; + export interface S2DForecastDisplaySkillFieldCheckboxProps { tooltip?: React.ReactNode; } @@ -18,6 +20,12 @@ export const MaskLowSkillField = ( const dispatch = useAppDispatch(); const checked = useAppSelector(selectLowSkillVisibility()); + const forecastDisplay = useAppSelector( + (state) => state.climateVariable.data?.forecastDisplay, + ); + const isForecast = forecastDisplay === ForecastDisplays.FORECAST + || forecastDisplay === undefined; + const onCheckedChange = (checked: boolean) => { dispatch(setLowSkillVisibility({visible: checked})); }; @@ -29,6 +37,7 @@ export const MaskLowSkillField = ( const fieldProps = { checked, onCheckedChange, + disabled: !isForecast, ...propsRest, }; @@ -41,7 +50,7 @@ export const MaskLowSkillField = ( /> From 215c57161a94fe5923fc128098a8b80c5cae2139 Mon Sep 17 00:00:00 2001 From: Renoir Boulanger Date: Mon, 16 Mar 2026 13:15:36 -0400 Subject: [PATCH 03/13] CLIM-1291: Hide low skill layer when not Forecast --- apps/src/components/map-layers/low-skill-layer.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/src/components/map-layers/low-skill-layer.tsx b/apps/src/components/map-layers/low-skill-layer.tsx index 5fe60ec87..c6299034f 100644 --- a/apps/src/components/map-layers/low-skill-layer.tsx +++ b/apps/src/components/map-layers/low-skill-layer.tsx @@ -6,6 +6,7 @@ import { useS2D } from '@/hooks/use-s2d'; import { useAppSelector } from '@/app/hooks'; import { selectLowSkillVisibility } from '@/features/map/map-slice'; import { buildSkillLayerName, buildSkillLayerTime } from '@/lib/s2d'; +import { ForecastDisplays } from '@/types/climate-variable-interface'; import L from 'leaflet'; interface LowSkillLayerProps { @@ -19,6 +20,9 @@ const LowSkillLayer = ({ pane, }: LowSkillLayerProps): React.ReactElement | null => { const { climateVariable } = useClimateVariable(); + const forecastDisplay = climateVariable?.getForecastDisplay(); + const isForecast = forecastDisplay === ForecastDisplays.FORECAST + || forecastDisplay === undefined; const { releaseDate } = useS2D(); const isLowSkillMasked = !useAppSelector(selectLowSkillVisibility()); const { @@ -49,7 +53,7 @@ const LowSkillLayer = ({ // attributes change. return useMemo( () => { - if (isLowSkillMasked || !layerName || !timeValue) { + if (!isForecast || isLowSkillMasked || !layerName || !timeValue) { return null; } @@ -78,7 +82,7 @@ const LowSkillLayer = ({ // above takes care of that). But we still use it as an initial value. // // eslint-disable-next-line react-hooks/exhaustive-deps - [pane, isLowSkillMasked, layerName, timeValue] + [pane, isForecast, isLowSkillMasked, layerName, timeValue] ); }; From 8d8669abc5ed6be6bbbf8498a8d15618433625fb Mon Sep 17 00:00:00 2001 From: Renoir Boulanger Date: Mon, 16 Mar 2026 13:33:14 -0400 Subject: [PATCH 04/13] CLIM-1291: Remove undefined fallback from isForecast undefined is a transient React re-render state, not a valid forecastDisplay value. Only positively match ForecastDisplays.FORECAST. --- apps/src/components/fields/skill.tsx | 3 +-- apps/src/components/map-layers/low-skill-layer.tsx | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/src/components/fields/skill.tsx b/apps/src/components/fields/skill.tsx index 38c0115a2..0bf57af18 100644 --- a/apps/src/components/fields/skill.tsx +++ b/apps/src/components/fields/skill.tsx @@ -23,8 +23,7 @@ export const MaskLowSkillField = ( const forecastDisplay = useAppSelector( (state) => state.climateVariable.data?.forecastDisplay, ); - const isForecast = forecastDisplay === ForecastDisplays.FORECAST - || forecastDisplay === undefined; + const isForecast = forecastDisplay === ForecastDisplays.FORECAST; const onCheckedChange = (checked: boolean) => { dispatch(setLowSkillVisibility({visible: checked})); diff --git a/apps/src/components/map-layers/low-skill-layer.tsx b/apps/src/components/map-layers/low-skill-layer.tsx index c6299034f..92744f450 100644 --- a/apps/src/components/map-layers/low-skill-layer.tsx +++ b/apps/src/components/map-layers/low-skill-layer.tsx @@ -21,8 +21,7 @@ const LowSkillLayer = ({ }: LowSkillLayerProps): React.ReactElement | null => { const { climateVariable } = useClimateVariable(); const forecastDisplay = climateVariable?.getForecastDisplay(); - const isForecast = forecastDisplay === ForecastDisplays.FORECAST - || forecastDisplay === undefined; + const isForecast = forecastDisplay === ForecastDisplays.FORECAST; const { releaseDate } = useS2D(); const isLowSkillMasked = !useAppSelector(selectLowSkillVisibility()); const { From 9d15407d42b30c026919ad5473e5e6b2bbe57aa3 Mon Sep 17 00:00:00 2001 From: Renoir Boulanger Date: Mon, 16 Mar 2026 13:42:45 -0400 Subject: [PATCH 05/13] CLIM-1291: One-per-line for conditional and deps array --- .../src/components/map-layers/low-skill-layer.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/src/components/map-layers/low-skill-layer.tsx b/apps/src/components/map-layers/low-skill-layer.tsx index 92744f450..e278717ba 100644 --- a/apps/src/components/map-layers/low-skill-layer.tsx +++ b/apps/src/components/map-layers/low-skill-layer.tsx @@ -52,7 +52,12 @@ const LowSkillLayer = ({ // attributes change. return useMemo( () => { - if (!isForecast || isLowSkillMasked || !layerName || !timeValue) { + if ( + !isForecast + || isLowSkillMasked + || !layerName + || !timeValue + ) { return null; } @@ -81,7 +86,13 @@ const LowSkillLayer = ({ // above takes care of that). But we still use it as an initial value. // // eslint-disable-next-line react-hooks/exhaustive-deps - [pane, isForecast, isLowSkillMasked, layerName, timeValue] + [ + pane, + isForecast, + isLowSkillMasked, + layerName, + timeValue, + ] ); }; From 0600e4943a30d28294e54210474977159b8392a0 Mon Sep 17 00:00:00 2001 From: Renoir Boulanger Date: Mon, 16 Mar 2026 13:47:06 -0400 Subject: [PATCH 06/13] CLIM-1291: ordering --- apps/src/components/map-layers/low-skill-layer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/components/map-layers/low-skill-layer.tsx b/apps/src/components/map-layers/low-skill-layer.tsx index e278717ba..c424e1c57 100644 --- a/apps/src/components/map-layers/low-skill-layer.tsx +++ b/apps/src/components/map-layers/low-skill-layer.tsx @@ -87,10 +87,10 @@ const LowSkillLayer = ({ // // eslint-disable-next-line react-hooks/exhaustive-deps [ - pane, isForecast, isLowSkillMasked, layerName, + pane, timeValue, ] ); From aa3b01ddd17ea33442497c20c8e783043df97810 Mon Sep 17 00:00:00 2001 From: Renoir Boulanger Date: Mon, 16 Mar 2026 14:43:33 -0400 Subject: [PATCH 07/13] CLIM-1291: Mute checkbox red when disabled Swap brand-red background/border to neutral-grey-medium when disabled + checked, so the checkbox is visually obviously inactive. --- apps/src/components/fields/skill.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/components/fields/skill.tsx b/apps/src/components/fields/skill.tsx index 0bf57af18..88432120a 100644 --- a/apps/src/components/fields/skill.tsx +++ b/apps/src/components/fields/skill.tsx @@ -44,7 +44,7 @@ export const MaskLowSkillField = (