This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-7256-a96bf3e70c0e9d5bf72402637633b36e99049a25
in repository https://gitbox.apache.org/repos/asf/texera.git

commit 5e41efae2650c3e70d7490b15bb1fdf9b1983568
Author: Kary Zheng <[email protected]>
AuthorDate: Fri Aug 7 12:20:45 2026 -0700

    feat(time-series): declare Plot Type's two values as a schema enum (#7256)
    
    ### What changes were proposed in this PR?
    
    `plotType` — the **Plot Type** field — was a plain string whose two
    legal values existed only in its description, so the form rendered a
    free-text box. It now declares them as a schema `enum`, which ngx-formly
    renders as a dropdown; `ECDFPlotOpDesc` already declares its string
    modes this way, so no frontend change is involved.
    
    The property's declared name stays `line`, since that is what saved
    workflows carry, and the generator's fallback is untouched.
    
    ### Why are the changes needed?
    
    The value is matched exactly — `if (plotType == "area") "px.area" else
    "px.line"` — so anything else draws a line chart and reports nothing.
    Generating the module on `main`:
    
    | Plot Type | Generated call |
    | --- | --- |
    | `area` | `px.area(table, ...)` |
    | `Area` — capitalised | `px.line(table, ...)` |
    | `aera` — one letter off | `px.line(table, ...)` |
    
    `Area` is the sharp case: likely to be typed, and the result is
    indistinguishable from having asked for a line chart.
    
    ### Any related issues, documentation, discussions?
    
    Closes #7251
    
    ### How was this PR tested?
    
    `WorkflowOperator/compile`, `scalafmtCheckAll`, and
    `TimeSeriesOpDescSpec` (13 tests). That suite pins the fallback by
    setting `plotType = "bar"` directly and asserting `px.line`; it bypasses
    the form, so it still passes.
    
    ### Does this PR introduce any user-facing change?
    
    Yes. Plot Type becomes a dropdown of `line` and `area`. Saved workflows
    carrying either are unaffected; one carrying anything else was already
    drawn as a line and still is.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Claude Opus 5)
    
    ---------
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
    Co-authored-by: Xuan Gu <[email protected]>
---
 .../visualization/timeSeriesplot/TimeSeriesOpDesc.scala |  5 +++++
 .../operator/timeSeriesPlot/TimeSeriesOpDescSpec.scala  | 17 ++++++++++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/timeSeriesplot/TimeSeriesOpDesc.scala
 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/timeSeriesplot/TimeSeriesOpDesc.scala
index 572fef82e4..7d09972717 100644
--- 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/timeSeriesplot/TimeSeriesOpDesc.scala
+++ 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/visualization/timeSeriesplot/TimeSeriesOpDesc.scala
@@ -59,9 +59,14 @@ class TimeSeriesOpDesc extends PythonOperatorDescriptor {
   @AutofillAttributeName
   var facetColumn: EncodableString = "No Selection"
 
+  // Declared as a schema enum rather than named only in the description: the 
code
+  // below reads exactly two values (`plotType == "area"`, else a line), so a 
free-text
+  // box let a typo through and silently drew a line -- no error, just not the 
chart
+  // that was asked for.
   @JsonProperty(value = "line", defaultValue = "line", required = true)
   @JsonSchemaTitle("Plot Type")
   @JsonPropertyDescription("Select the type of time series plot (line, area).")
+  @JsonSchemaInject(json = """{ "enum": ["line", "area"], "default": "line" 
}""")
   @NotBlank(message = "Plot Type cannot be empty")
   var plotType: String = "line"
 
diff --git 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/timeSeriesPlot/TimeSeriesOpDescSpec.scala
 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/timeSeriesPlot/TimeSeriesOpDescSpec.scala
index 75478edf23..7a02ce2275 100644
--- 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/timeSeriesPlot/TimeSeriesOpDescSpec.scala
+++ 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/timeSeriesPlot/TimeSeriesOpDescSpec.scala
@@ -22,13 +22,14 @@ package org.apache.texera.amber.operator.timeSeriesPlot
 import org.apache.texera.amber.core.tuple.{AttributeType, Schema}
 import org.apache.texera.amber.core.workflow.PortIdentity
 import org.apache.texera.amber.operator.LogicalOp
-import org.apache.texera.amber.operator.metadata.OperatorGroupConstants
+import org.apache.texera.amber.operator.metadata.{OperatorGroupConstants, 
OperatorMetadataGenerator}
 import 
org.apache.texera.amber.operator.visualization.timeSeriesplot.TimeSeriesOpDesc
 import org.apache.texera.amber.util.JSONUtils.objectMapper
 import org.scalatest.funsuite.AnyFunSuite
 
 import java.nio.charset.StandardCharsets
 import java.util.Base64
+import scala.jdk.CollectionConverters._
 
 class TimeSeriesOpDescSpec extends AnyFunSuite {
 
@@ -186,6 +187,20 @@ class TimeSeriesOpDescSpec extends AnyFunSuite {
     assert(!new TimeSeriesOpDesc().showRangeSlider)
   }
 
+  test("the generated schema offers Plot Type as exactly the two values the 
code reads") {
+    // The property is declared under the name "line", which is what saved 
workflows carry.
+    val plotType = OperatorMetadataGenerator
+      .generateOperatorJsonSchema(classOf[TimeSeriesOpDesc])
+      .path("properties")
+      .path("line")
+
+    assert(plotType.path("enum").elements().asScala.map(_.asText()).toList == 
List("line", "area"))
+    // the schema default is what the descriptor itself starts at, so an 
untouched form
+    // and an untouched descriptor draw the same chart
+    assert(plotType.path("default").asText() == "line")
+    assert(plotType.path("default").asText() == new 
TimeSeriesOpDesc().plotType)
+  }
+
   // --- generated code shape 
----------------------------------------------------
 
   test("generated code coerces the time column, sorts by it, and guards both 
empty cases") {

Reply via email to