@cdc/chart 4.25.7 → 4.25.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +9 -0
- package/dist/cdcchart.js +39551 -37016
- package/examples/feature/__data__/planet-example-data.json +0 -30
- package/examples/grouped-bar-test.json +400 -0
- package/examples/private/d.json +382 -0
- package/examples/private/example-2.json +49784 -0
- package/examples/private/f2.json +1 -0
- package/examples/private/f4.json +1577 -0
- package/examples/private/forecast.json +1180 -0
- package/examples/private/lollipop.json +468 -0
- package/examples/private/new.json +48756 -0
- package/examples/private/pie-chart-legend.json +904 -0
- package/examples/suppressed_tooltip.json +480 -0
- package/index.html +10 -22
- package/package.json +25 -7
- package/src/CdcChart.tsx +10 -4
- package/src/CdcChartComponent.tsx +188 -32
- package/src/_stories/Chart.Anchors.stories.tsx +2 -2
- package/src/_stories/Chart.BoxPlot.stories.tsx +1 -1
- package/src/_stories/Chart.CI.stories.tsx +1 -1
- package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
- package/src/_stories/Chart.DynamicSeries.stories.tsx +2 -2
- package/src/_stories/Chart.Filters.stories.tsx +2 -2
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
- package/src/_stories/Chart.Patterns.stories.tsx +19 -0
- package/src/_stories/Chart.ScatterPlot.stories.tsx +1 -1
- package/src/_stories/Chart.stories.tsx +8 -5
- package/src/_stories/Chart.tooltip.stories.tsx +1 -1
- package/src/_stories/ChartAnnotation.stories.tsx +1 -1
- package/src/_stories/ChartAxisLabels.stories.tsx +2 -2
- package/src/_stories/ChartAxisTitles.stories.tsx +2 -2
- package/src/_stories/ChartEditor.stories.tsx +60 -60
- package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
- package/src/_stories/ChartLine.Symbols.stories.tsx +1 -1
- package/src/_stories/ChartPrefixSuffix.stories.tsx +2 -2
- package/src/_stories/_mock/stacked-pattern-test.json +520 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -0
- package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +170 -25
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +139 -6
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +215 -73
- package/src/components/BarChart/components/BarChart.Vertical.tsx +172 -23
- package/src/components/BarChart/helpers/index.ts +43 -4
- package/src/components/BarChart/helpers/lollipopColors.ts +27 -0
- package/src/components/BarChart/helpers/useBarChart.ts +25 -3
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +2 -1
- package/src/components/Brush/BrushChart.tsx +65 -10
- package/src/components/Brush/BrushController.tsx +37 -5
- package/src/components/Brush/types.tsx +8 -0
- package/src/components/DeviationBar.jsx +9 -6
- package/src/components/EditorPanel/EditorPanel.tsx +364 -39
- package/src/components/EditorPanel/EditorPanelContext.ts +3 -0
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +2 -2
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +414 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +30 -54
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +115 -120
- package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
- package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +0 -8
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +49 -48
- package/src/components/Forecasting/Forecasting.tsx +36 -6
- package/src/components/ForestPlot/ForestPlot.tsx +11 -7
- package/src/components/ForestPlot/ForestPlotProps.ts +1 -1
- package/src/components/Legend/Legend.Component.tsx +110 -2
- package/src/components/Legend/Legend.tsx +3 -1
- package/src/components/Legend/helpers/createFormatLabels.tsx +230 -171
- package/src/components/LegendWrapper.tsx +1 -1
- package/src/components/LineChart/components/LineChart.BumpCircle.tsx +27 -26
- package/src/components/LineChart/components/LineChart.Circle.tsx +2 -2
- package/src/components/LineChart/index.tsx +2 -2
- package/src/components/LinearChart.tsx +26 -9
- package/src/components/PairedBarChart.jsx +6 -4
- package/src/components/PieChart/PieChart.tsx +170 -54
- package/src/components/Sankey/components/Sankey.tsx +7 -1
- package/src/components/ScatterPlot/ScatterPlot.jsx +32 -4
- package/src/data/initial-state.js +315 -292
- package/src/helpers/buildForecastPaletteMappings.ts +112 -0
- package/src/helpers/buildForecastPaletteOptions.ts +109 -0
- package/src/helpers/getColorScale.ts +72 -8
- package/src/helpers/getNewRuntime.ts +1 -1
- package/src/helpers/getTransformedData.ts +1 -1
- package/src/hooks/useChartHoverAnalytics.tsx +44 -0
- package/src/hooks/useReduceData.ts +105 -70
- package/src/hooks/useTooltip.tsx +58 -16
- package/src/index.jsx +6 -3
- package/src/scss/main.scss +12 -0
- package/src/store/chart.reducer.ts +1 -1
- package/src/test/CdcChart.test.jsx +8 -3
- package/src/types/ChartConfig.ts +30 -6
- package/src/types/ChartContext.ts +1 -0
- package/vite.config.js +1 -1
- package/vitest.config.ts +16 -0
- package/src/coreStyles_chart.scss +0 -3
- package/src/helpers/configHelpers.ts +0 -28
- package/src/helpers/generateColorsArray.ts +0 -8
- package/src/hooks/useColorPalette.js +0 -76
|
@@ -23,9 +23,11 @@ import { Select, TextField, CheckBox } from '@cdc/core/components/EditorPanel/In
|
|
|
23
23
|
import MultiSelect from '@cdc/core/components/MultiSelect'
|
|
24
24
|
import { viewports } from '@cdc/core/helpers/getViewport'
|
|
25
25
|
import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
|
|
26
|
+
import PanelMarkup from '@cdc/core/components/EditorPanel/components/PanelMarkup'
|
|
26
27
|
|
|
27
28
|
// chart components
|
|
28
29
|
import Panels from './components/Panels'
|
|
30
|
+
import PaletteConversionModal from '@cdc/core/components/PaletteConversionModal'
|
|
29
31
|
|
|
30
32
|
// cdc additional
|
|
31
33
|
import { useEditorPermissions } from './useEditorPermissions'
|
|
@@ -45,9 +47,13 @@ import EditorPanelContext from './EditorPanelContext'
|
|
|
45
47
|
import _ from 'lodash'
|
|
46
48
|
import { adjustedSymbols as symbolCodes } from '@cdc/core/helpers/footnoteSymbols'
|
|
47
49
|
import { updateFieldRankByValue } from './helpers/updateFieldRankByValue'
|
|
50
|
+
import cloneConfig from '@cdc/core/helpers/cloneConfig'
|
|
48
51
|
import FootnotesEditor from '@cdc/core/components/EditorPanel/FootnotesEditor'
|
|
49
52
|
import { Datasets } from '@cdc/core/types/DataSet'
|
|
50
53
|
import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
|
|
54
|
+
import { paletteMigrationMap, twoColorPaletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
|
|
55
|
+
import { isV1Palette, migratePaletteWithMap } from '@cdc/core/helpers/palettes/utils'
|
|
56
|
+
import { USE_V2_MIGRATION } from '@cdc/core/helpers/constants'
|
|
51
57
|
|
|
52
58
|
interface PreliminaryProps {
|
|
53
59
|
config: ChartConfig
|
|
@@ -825,6 +831,14 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
825
831
|
|
|
826
832
|
const [displayPanel, setDisplayPanel] = useState(true)
|
|
827
833
|
const [displayViewportOverrides, setDisplayViewportOverrides] = useState(false)
|
|
834
|
+
const [showConversionModal, setShowConversionModal] = useState(false)
|
|
835
|
+
const [pendingPaletteSelection, setPendingPaletteSelection] = useState<{
|
|
836
|
+
palette: string
|
|
837
|
+
action: () => void
|
|
838
|
+
seriesIndex?: number
|
|
839
|
+
stageIndex?: number
|
|
840
|
+
type?: 'general' | 'twoColor' | 'forecast'
|
|
841
|
+
} | null>(null)
|
|
828
842
|
|
|
829
843
|
const setLollipopShape = shape => {
|
|
830
844
|
updateConfig({
|
|
@@ -892,8 +906,36 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
892
906
|
|
|
893
907
|
const getColumns = (filter = true) => {
|
|
894
908
|
let columns = {}
|
|
895
|
-
|
|
896
|
-
|
|
909
|
+
|
|
910
|
+
// Try multiple data sources in order of preference
|
|
911
|
+
let dataToUse = []
|
|
912
|
+
|
|
913
|
+
if (unfilteredData && unfilteredData.length > 0) {
|
|
914
|
+
// First preference: unfilteredData from context
|
|
915
|
+
dataToUse = unfilteredData
|
|
916
|
+
} else if (isDashboard && datasets && config.dataKey && datasets[config.dataKey]?.data?.length > 0) {
|
|
917
|
+
// Second preference: data from datasets in dashboard mode
|
|
918
|
+
dataToUse = datasets[config.dataKey].data
|
|
919
|
+
} else if (rawData && rawData.length > 0) {
|
|
920
|
+
// Third preference: rawData from context
|
|
921
|
+
dataToUse = rawData
|
|
922
|
+
} else if (data && data.length > 0) {
|
|
923
|
+
// Fourth preference: transformedData from context
|
|
924
|
+
dataToUse = data
|
|
925
|
+
} else if (config.data && config.data.length > 0) {
|
|
926
|
+
// Fifth preference: data directly from config
|
|
927
|
+
dataToUse = config.data
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// If we still don't have data, return empty array
|
|
931
|
+
if (!dataToUse || dataToUse.length === 0) {
|
|
932
|
+
return []
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
dataToUse.forEach(row => {
|
|
936
|
+
if (row && typeof row === 'object') {
|
|
937
|
+
Object.keys(row).forEach(columnName => (columns[columnName] = true))
|
|
938
|
+
}
|
|
897
939
|
})
|
|
898
940
|
|
|
899
941
|
if (filter) {
|
|
@@ -975,18 +1017,18 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
975
1017
|
|
|
976
1018
|
// prettier-ignore
|
|
977
1019
|
const {
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1020
|
+
highlightedBarValues,
|
|
1021
|
+
highlightedSeriesValues,
|
|
1022
|
+
handleUpdateHighlightedBar,
|
|
1023
|
+
handleAddNewHighlightedBar,
|
|
1024
|
+
handleRemoveHighlightedBar,
|
|
1025
|
+
handleUpdateHighlightedBarColor,
|
|
1026
|
+
handleHighlightedBarLegendLabel,
|
|
1027
|
+
handleUpdateHighlightedBorderWidth
|
|
1028
|
+
} = useHighlightedBars(config, updateConfig)
|
|
987
1029
|
|
|
988
1030
|
const convertStateToConfig = () => {
|
|
989
|
-
let strippedState =
|
|
1031
|
+
let strippedState = cloneConfig(config)
|
|
990
1032
|
if (false === missingRequiredSections(config)) {
|
|
991
1033
|
delete strippedState.newViz
|
|
992
1034
|
}
|
|
@@ -1093,6 +1135,289 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
1093
1135
|
const section = config.orientation === 'horizontal' ? 'xAxis' : 'yAxis'
|
|
1094
1136
|
const [warningMsg, setWarningMsg] = useState({ maxMsg: '', minMsg: '', rightMaxMessage: '', minMsgRight: '' })
|
|
1095
1137
|
|
|
1138
|
+
// Palette migration functions
|
|
1139
|
+
const handlePaletteSelection = (palette: string) => {
|
|
1140
|
+
try {
|
|
1141
|
+
// Check if config exists and has basic structure
|
|
1142
|
+
if (!config) {
|
|
1143
|
+
console.error('COVE: Config is undefined in handlePaletteSelection')
|
|
1144
|
+
return
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// Check if it's a v1 palette configuration
|
|
1148
|
+
const isV1PaletteConfig = isV1Palette(config)
|
|
1149
|
+
|
|
1150
|
+
const executeSelection = () => {
|
|
1151
|
+
const _newConfig = cloneConfig(config)
|
|
1152
|
+
if (!_newConfig.general.palette) {
|
|
1153
|
+
_newConfig.general.palette = {}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// If v2 migration is disabled, use the original palette name and keep v1 version
|
|
1157
|
+
if (!USE_V2_MIGRATION) {
|
|
1158
|
+
_newConfig.general.palette.name = palette
|
|
1159
|
+
_newConfig.general.palette.version = '1.0'
|
|
1160
|
+
} else {
|
|
1161
|
+
// V2 migration logic
|
|
1162
|
+
const migratedName = palette ? migratePaletteWithMap(palette, paletteMigrationMap, false) : undefined
|
|
1163
|
+
_newConfig.general.palette.name = migratedName
|
|
1164
|
+
if (isV1PaletteConfig) {
|
|
1165
|
+
_newConfig.general.palette.version = '2.0'
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
updateConfig(_newConfig)
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
if (isV1PaletteConfig) {
|
|
1172
|
+
setPendingPaletteSelection({ palette, action: executeSelection, type: 'general' })
|
|
1173
|
+
setShowConversionModal(true)
|
|
1174
|
+
} else {
|
|
1175
|
+
executeSelection()
|
|
1176
|
+
}
|
|
1177
|
+
} catch (error) {
|
|
1178
|
+
console.error('COVE: Error in handlePaletteSelection:', error)
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// Two-color palette migration function
|
|
1183
|
+
const handleTwoColorPaletteSelection = (palette: string) => {
|
|
1184
|
+
try {
|
|
1185
|
+
// Check if config exists and has basic structure
|
|
1186
|
+
if (!config) {
|
|
1187
|
+
console.error('COVE: Config is undefined in handleTwoColorPaletteSelection')
|
|
1188
|
+
return
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
// Check if it's a v1 palette configuration
|
|
1192
|
+
const isV1PaletteConfig = isV1Palette(config)
|
|
1193
|
+
|
|
1194
|
+
const executeSelection = () => {
|
|
1195
|
+
const _newConfig = cloneConfig(config)
|
|
1196
|
+
if (!_newConfig.twoColor) {
|
|
1197
|
+
_newConfig.twoColor = { palette: '', isPaletteReversed: false }
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// If v2 migration is disabled, use the original palette name and keep v1 version
|
|
1201
|
+
if (!USE_V2_MIGRATION) {
|
|
1202
|
+
_newConfig.twoColor.palette = palette
|
|
1203
|
+
if (!_newConfig.general) {
|
|
1204
|
+
_newConfig.general = {}
|
|
1205
|
+
}
|
|
1206
|
+
if (!_newConfig.general.palette) {
|
|
1207
|
+
_newConfig.general.palette = {}
|
|
1208
|
+
}
|
|
1209
|
+
_newConfig.general.palette.version = '1.0'
|
|
1210
|
+
} else {
|
|
1211
|
+
// V2 migration logic
|
|
1212
|
+
const migratedPaletteName = isV1PaletteConfig
|
|
1213
|
+
? migratePaletteWithMap(palette, twoColorPaletteMigrationMap, false)
|
|
1214
|
+
: palette
|
|
1215
|
+
|
|
1216
|
+
_newConfig.twoColor.palette = migratedPaletteName
|
|
1217
|
+
|
|
1218
|
+
if (isV1PaletteConfig) {
|
|
1219
|
+
if (!_newConfig.general) {
|
|
1220
|
+
_newConfig.general = {}
|
|
1221
|
+
}
|
|
1222
|
+
if (!_newConfig.general.palette) {
|
|
1223
|
+
_newConfig.general.palette = {}
|
|
1224
|
+
}
|
|
1225
|
+
_newConfig.general.palette.version = '2.0'
|
|
1226
|
+
|
|
1227
|
+
// Create backup for rollback functionality (consistent with standard format)
|
|
1228
|
+
if (!_newConfig.general.palette.backups) {
|
|
1229
|
+
_newConfig.general.palette.backups = []
|
|
1230
|
+
}
|
|
1231
|
+
_newConfig.general.palette.backups.push({
|
|
1232
|
+
name: config.twoColor?.palette || palette,
|
|
1233
|
+
version: '1.0',
|
|
1234
|
+
isReversed: false,
|
|
1235
|
+
type: 'twoColor'
|
|
1236
|
+
})
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
updateConfig(_newConfig)
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
if (isV1PaletteConfig) {
|
|
1243
|
+
setPendingPaletteSelection({ palette, action: executeSelection, type: 'twoColor' })
|
|
1244
|
+
setShowConversionModal(true)
|
|
1245
|
+
} else {
|
|
1246
|
+
executeSelection()
|
|
1247
|
+
}
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
console.error('COVE: Error in handleTwoColorPaletteSelection:', error)
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// Forecast palette selection - includes v1/v2 migration modal logic
|
|
1254
|
+
const handleForecastPaletteSelection = (palette: string, seriesIndex: number, stageIndex: number) => {
|
|
1255
|
+
try {
|
|
1256
|
+
if (!config) {
|
|
1257
|
+
console.error('COVE: Config is undefined in handleForecastPaletteSelection')
|
|
1258
|
+
return
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// Check if it's a v1 palette configuration
|
|
1262
|
+
const isV1PaletteConfig = isV1Palette(config)
|
|
1263
|
+
|
|
1264
|
+
const executeSelection = () => {
|
|
1265
|
+
const copyOfSeries = [...config.series]
|
|
1266
|
+
const copyOfStages = [...(copyOfSeries[seriesIndex].stages || [])]
|
|
1267
|
+
copyOfStages[stageIndex] = { ...copyOfStages[stageIndex], color: palette }
|
|
1268
|
+
copyOfSeries[seriesIndex] = { ...copyOfSeries[seriesIndex], stages: copyOfStages }
|
|
1269
|
+
|
|
1270
|
+
const _newConfig = cloneConfig(config)
|
|
1271
|
+
_newConfig.series = copyOfSeries
|
|
1272
|
+
|
|
1273
|
+
// If this is the first v2 palette selection, upgrade to v2
|
|
1274
|
+
if (isV1PaletteConfig && USE_V2_MIGRATION) {
|
|
1275
|
+
if (!_newConfig.general) {
|
|
1276
|
+
_newConfig.general = {}
|
|
1277
|
+
}
|
|
1278
|
+
if (!_newConfig.general.palette) {
|
|
1279
|
+
_newConfig.general.palette = {}
|
|
1280
|
+
}
|
|
1281
|
+
_newConfig.general.palette.version = '2.0'
|
|
1282
|
+
|
|
1283
|
+
// Forecast-specific migration map for v1 → v2 palette names (all lowercase-hyphen format)
|
|
1284
|
+
const forecastPaletteMigrationMap: Record<string, string> = {
|
|
1285
|
+
// Sequential Blue variants → sequential-blue
|
|
1286
|
+
'sequential-blue': 'sequential-blue',
|
|
1287
|
+
'sequential-blue-two': 'sequential-blue',
|
|
1288
|
+
'sequential-blue-three': 'sequential-blue',
|
|
1289
|
+
'sequential-blue-2-(mpx)': 'sequential-blue',
|
|
1290
|
+
'sequential-blue-2-mpx': 'sequential-blue',
|
|
1291
|
+
// Sequential Orange variants → sequential-orange
|
|
1292
|
+
'sequential-orange': 'sequential-orange',
|
|
1293
|
+
'sequential-orange-two': 'sequential-orange',
|
|
1294
|
+
'sequential-orange-(mpx)': 'sequential-orange',
|
|
1295
|
+
'sequential-orange-mpx': 'sequential-orange',
|
|
1296
|
+
// Other sequential palettes (no variants, just normalize)
|
|
1297
|
+
'sequential-green': 'sequential-green',
|
|
1298
|
+
'sequential-purple': 'sequential-purple',
|
|
1299
|
+
'sequential-teal': 'sequential-teal',
|
|
1300
|
+
// Reverse variants - Sequential Blue
|
|
1301
|
+
'sequential-bluereverse': 'sequential-bluereverse',
|
|
1302
|
+
'sequential-blue-reverse': 'sequential-bluereverse',
|
|
1303
|
+
'sequential-blue-tworeverse': 'sequential-bluereverse',
|
|
1304
|
+
'sequential-blue-two-reverse': 'sequential-bluereverse',
|
|
1305
|
+
'sequential-blue-threereverse': 'sequential-bluereverse',
|
|
1306
|
+
'sequential-blue-three-reverse': 'sequential-bluereverse',
|
|
1307
|
+
'sequential-blue-2-(mpx)reverse': 'sequential-bluereverse',
|
|
1308
|
+
'sequential-blue-2-(mpx)-reverse': 'sequential-bluereverse',
|
|
1309
|
+
'sequential-blue-2-mpxreverse': 'sequential-bluereverse',
|
|
1310
|
+
'sequential-blue-2-mpx-reverse': 'sequential-bluereverse',
|
|
1311
|
+
// Reverse variants - Sequential Orange
|
|
1312
|
+
'sequential-orangereverse': 'sequential-orangereverse',
|
|
1313
|
+
'sequential-orange-reverse': 'sequential-orangereverse',
|
|
1314
|
+
'sequential-orange-tworeverse': 'sequential-orangereverse',
|
|
1315
|
+
'sequential-orange-two-reverse': 'sequential-orangereverse',
|
|
1316
|
+
'sequential-orange-(mpx)reverse': 'sequential-orangereverse',
|
|
1317
|
+
'sequential-orange-(mpx)-reverse': 'sequential-orangereverse',
|
|
1318
|
+
'sequential-orange-mpxreverse': 'sequential-orangereverse',
|
|
1319
|
+
'sequential-orange-mpx-reverse': 'sequential-orangereverse',
|
|
1320
|
+
// Reverse variants - Other sequential palettes
|
|
1321
|
+
'sequential-greenreverse': 'sequential-greenreverse',
|
|
1322
|
+
'sequential-green-reverse': 'sequential-greenreverse',
|
|
1323
|
+
'sequential-purplereverse': 'sequential-purplereverse',
|
|
1324
|
+
'sequential-purple-reverse': 'sequential-purplereverse',
|
|
1325
|
+
'sequential-tealreverse': 'sequential-tealreverse',
|
|
1326
|
+
'sequential-teal-reverse': 'sequential-tealreverse'
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// Migrate and normalize all forecast stage colors to v2 format
|
|
1330
|
+
_newConfig.series.forEach((series: any) => {
|
|
1331
|
+
if (series.type === 'Forecasting' && series.stages) {
|
|
1332
|
+
series.stages.forEach((stage: any) => {
|
|
1333
|
+
if (stage.color) {
|
|
1334
|
+
// First, try to migrate using the map
|
|
1335
|
+
const migrated = forecastPaletteMigrationMap[stage.color] || stage.color
|
|
1336
|
+
// Then normalize to lowercase with hyphens
|
|
1337
|
+
stage.color = migrated.toLowerCase().replace(/ /g, '-').replace(/_/g, '-')
|
|
1338
|
+
}
|
|
1339
|
+
})
|
|
1340
|
+
}
|
|
1341
|
+
})
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
updateConfig(_newConfig)
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
if (isV1PaletteConfig) {
|
|
1348
|
+
setPendingPaletteSelection({ palette, action: executeSelection, type: 'forecast', seriesIndex, stageIndex })
|
|
1349
|
+
setShowConversionModal(true)
|
|
1350
|
+
} else {
|
|
1351
|
+
executeSelection()
|
|
1352
|
+
}
|
|
1353
|
+
} catch (error) {
|
|
1354
|
+
console.error('COVE: Error in handleForecastPaletteSelection:', error)
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
// Modal handlers
|
|
1359
|
+
const handleConversionConfirm = () => {
|
|
1360
|
+
if (pendingPaletteSelection) {
|
|
1361
|
+
pendingPaletteSelection.action()
|
|
1362
|
+
}
|
|
1363
|
+
setShowConversionModal(false)
|
|
1364
|
+
setPendingPaletteSelection(null)
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
const handleConversionCancel = () => {
|
|
1368
|
+
// Don't update config - just close modal and discard pending selection
|
|
1369
|
+
setShowConversionModal(false)
|
|
1370
|
+
setPendingPaletteSelection(null)
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
const handleReturnToV1 = () => {
|
|
1374
|
+
if (pendingPaletteSelection) {
|
|
1375
|
+
const _newConfig = cloneConfig(config)
|
|
1376
|
+
const { palette, type } = pendingPaletteSelection
|
|
1377
|
+
|
|
1378
|
+
// Handle based on palette type
|
|
1379
|
+
if (type === 'forecast') {
|
|
1380
|
+
// Forecast palette selection
|
|
1381
|
+
const { seriesIndex, stageIndex } = pendingPaletteSelection
|
|
1382
|
+
if (seriesIndex !== undefined && stageIndex !== undefined) {
|
|
1383
|
+
const copyOfSeries = [..._newConfig.series]
|
|
1384
|
+
const copyOfStages = [...copyOfSeries[seriesIndex].stages]
|
|
1385
|
+
copyOfStages[stageIndex] = { ...copyOfStages[stageIndex], color: palette }
|
|
1386
|
+
copyOfSeries[seriesIndex] = { ...copyOfSeries[seriesIndex], stages: copyOfStages }
|
|
1387
|
+
_newConfig.series = copyOfSeries
|
|
1388
|
+
}
|
|
1389
|
+
} else if (type === 'twoColor') {
|
|
1390
|
+
// Two-color palette selection
|
|
1391
|
+
if (!_newConfig.twoColor) {
|
|
1392
|
+
_newConfig.twoColor = { palette: '', isPaletteReversed: false }
|
|
1393
|
+
}
|
|
1394
|
+
_newConfig.twoColor.palette = palette
|
|
1395
|
+
} else {
|
|
1396
|
+
// General palette selection (type === 'general' or undefined for backwards compatibility)
|
|
1397
|
+
if (!_newConfig.general) {
|
|
1398
|
+
_newConfig.general = {}
|
|
1399
|
+
}
|
|
1400
|
+
if (!_newConfig.general.palette) {
|
|
1401
|
+
_newConfig.general.palette = {}
|
|
1402
|
+
}
|
|
1403
|
+
_newConfig.general.palette.name = palette
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// Set version to V1
|
|
1407
|
+
if (!_newConfig.general) {
|
|
1408
|
+
_newConfig.general = {}
|
|
1409
|
+
}
|
|
1410
|
+
if (!_newConfig.general.palette) {
|
|
1411
|
+
_newConfig.general.palette = {}
|
|
1412
|
+
}
|
|
1413
|
+
_newConfig.general.palette.version = '1.0'
|
|
1414
|
+
|
|
1415
|
+
updateConfig(_newConfig)
|
|
1416
|
+
}
|
|
1417
|
+
setShowConversionModal(false)
|
|
1418
|
+
setPendingPaletteSelection(null)
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1096
1421
|
const validateMaxValue = () => {
|
|
1097
1422
|
const enteredValue = config[section].max
|
|
1098
1423
|
const enteredRightMax = config[section].rightMax
|
|
@@ -1232,18 +1557,6 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
1232
1557
|
)
|
|
1233
1558
|
}
|
|
1234
1559
|
})
|
|
1235
|
-
|
|
1236
|
-
let columnsByKey = {}
|
|
1237
|
-
config.data.forEach(datum => {
|
|
1238
|
-
Object.keys(datum).forEach(key => {
|
|
1239
|
-
columnsByKey[key] = columnsByKey[key] || []
|
|
1240
|
-
const value = typeof datum[key] === 'number' ? datum[key].toString() : datum[key]
|
|
1241
|
-
|
|
1242
|
-
if (columnsByKey[key].indexOf(value) === -1) {
|
|
1243
|
-
columnsByKey[key].push(value)
|
|
1244
|
-
}
|
|
1245
|
-
})
|
|
1246
|
-
})
|
|
1247
1560
|
}
|
|
1248
1561
|
|
|
1249
1562
|
// for pie charts
|
|
@@ -1261,18 +1574,6 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
1261
1574
|
)
|
|
1262
1575
|
}
|
|
1263
1576
|
})
|
|
1264
|
-
|
|
1265
|
-
let columnsByKey = {}
|
|
1266
|
-
data.forEach(datum => {
|
|
1267
|
-
Object.keys(datum).forEach(key => {
|
|
1268
|
-
columnsByKey[key] = columnsByKey[key] || []
|
|
1269
|
-
const value = typeof datum[key] === 'number' ? datum[key].toString() : datum[key]
|
|
1270
|
-
|
|
1271
|
-
if (columnsByKey[key].indexOf(value) === -1) {
|
|
1272
|
-
columnsByKey[key].push(value)
|
|
1273
|
-
}
|
|
1274
|
-
})
|
|
1275
|
-
})
|
|
1276
1577
|
}
|
|
1277
1578
|
|
|
1278
1579
|
const removeAdditionalColumn = columnName => {
|
|
@@ -1379,11 +1680,14 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
1379
1680
|
handleHighlightedBarLegendLabel,
|
|
1380
1681
|
handleUpdateHighlightedBar,
|
|
1381
1682
|
handleRemoveHighlightedBar,
|
|
1382
|
-
isPaletteReversed: config.
|
|
1683
|
+
isPaletteReversed: config.general?.palette?.isReversed,
|
|
1383
1684
|
highlightedSeriesValues,
|
|
1384
1685
|
handleUpdateHighlightedBorderWidth,
|
|
1385
1686
|
handleUpdateHighlightedBarColor,
|
|
1386
|
-
setLollipopShape
|
|
1687
|
+
setLollipopShape,
|
|
1688
|
+
handlePaletteSelection,
|
|
1689
|
+
handleTwoColorPaletteSelection,
|
|
1690
|
+
handleForecastPaletteSelection
|
|
1387
1691
|
}
|
|
1388
1692
|
if (isLoading) {
|
|
1389
1693
|
return <></>
|
|
@@ -1478,7 +1782,10 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
1478
1782
|
options={getColumns()}
|
|
1479
1783
|
/>
|
|
1480
1784
|
{config.series && config.series.length !== 0 && (
|
|
1481
|
-
<Panels.Series.Wrapper
|
|
1785
|
+
<Panels.Series.Wrapper
|
|
1786
|
+
getColumns={getColumns}
|
|
1787
|
+
handleForecastPaletteSelection={handleForecastPaletteSelection}
|
|
1788
|
+
>
|
|
1482
1789
|
<fieldset>
|
|
1483
1790
|
<legend className='edit-label float-left'>Displaying</legend>
|
|
1484
1791
|
<Tooltip style={{ textTransform: 'none' }}>
|
|
@@ -4126,6 +4433,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
4126
4433
|
</>
|
|
4127
4434
|
)}
|
|
4128
4435
|
<Panels.Visual name='Visual' />
|
|
4436
|
+
<Panels.PatternSettings name='PatternSettings' />
|
|
4129
4437
|
{/* Spark Line has no data table */}
|
|
4130
4438
|
{config.visualizationType !== 'Spark Line' && (
|
|
4131
4439
|
<AccordionItem>
|
|
@@ -4145,11 +4453,28 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
4145
4453
|
)}
|
|
4146
4454
|
<Panels.Annotate name='Text Annotations' />
|
|
4147
4455
|
{/* {(config.visualizationType === 'Bar' || config.visualizationType === 'Line') && <Panels.DateHighlighting name='Date Highlighting' />} */}
|
|
4456
|
+
<PanelMarkup
|
|
4457
|
+
name='Markup Variables'
|
|
4458
|
+
markupVariables={config.markupVariables || []}
|
|
4459
|
+
data={rawData}
|
|
4460
|
+
enableMarkupVariables={config.enableMarkupVariables || false}
|
|
4461
|
+
onMarkupVariablesChange={variables => updateField(null, null, 'markupVariables', variables)}
|
|
4462
|
+
onToggleEnable={enabled => updateField(null, null, 'enableMarkupVariables', enabled)}
|
|
4463
|
+
/>
|
|
4148
4464
|
</Accordion>
|
|
4149
4465
|
{config.type !== 'Spark Line' && (
|
|
4150
4466
|
<AdvancedEditor loadConfig={updateConfig} config={config} convertStateToConfig={convertStateToConfig} />
|
|
4151
4467
|
)}
|
|
4152
4468
|
</Layout.Sidebar>
|
|
4469
|
+
|
|
4470
|
+
{showConversionModal && (
|
|
4471
|
+
<PaletteConversionModal
|
|
4472
|
+
onConfirm={handleConversionConfirm}
|
|
4473
|
+
onCancel={handleConversionCancel}
|
|
4474
|
+
onReturnToV1={handleReturnToV1}
|
|
4475
|
+
paletteName={pendingPaletteSelection?.palette}
|
|
4476
|
+
/>
|
|
4477
|
+
)}
|
|
4153
4478
|
</ErrorBoundary>
|
|
4154
4479
|
</EditorPanelContext.Provider>
|
|
4155
4480
|
)
|
|
@@ -23,6 +23,9 @@ export type EditorPanelContext = {
|
|
|
23
23
|
isPaletteReversed?: boolean
|
|
24
24
|
handleRemoveHighlightedBar?: Function
|
|
25
25
|
setLollipopShape?: Function
|
|
26
|
+
handlePaletteSelection?: (palette: string) => void
|
|
27
|
+
handleTwoColorPaletteSelection?: (palette: string) => void
|
|
28
|
+
handleForecastPaletteSelection?: (palette: string, seriesIndex: number, stageIndex: number) => void
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
const EditorPanelContext = createContext<EditorPanelContext>(null)
|
|
@@ -67,8 +67,8 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
|
|
|
67
67
|
config.xAxis.type === 'date'
|
|
68
68
|
? new Date(config?.data?.[0]?.[config.xAxis.dataKey]).getTime()
|
|
69
69
|
: config.xAxis.type === 'categorical'
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
? '1/15/2016'
|
|
71
|
+
: '',
|
|
72
72
|
yKey: '',
|
|
73
73
|
dx: 20,
|
|
74
74
|
dy: -20,
|