Skip to content

Attributes typed _componentWithSelectableType tell authors nothing, and default to number when the surrounding type is not numeric #1825

Description

@dqnykamp

Problem

Several attributes are declared createComponentOfType: "_componentWithSelectableType", and the reference docs print that name verbatim as the attribute's type. _componentWithSelectableType is an internal abstract component; it tells an author nothing about what to write.

It affects 15 attributes across 9 components:

Component Attributes
<sequence>, <selectFromSequence>, <repeatForSequence>, <animateFromSequence> from, to
<slider> bindValueTo, initialValue
<substitute> match, replacement
<updateValue> newValue
<indexOf>, <searchSorted> target

Why it is more than a wording problem

The value's type is resolved at sugar time by ComponentWithSelectableType: its own type attribute, else the parent's type attribute, else the default number. So when the author does not state a type, the value is parsed as a number — even when everything around it is text.

That silently produces wrong answers rather than an error:

<textList name="words">apple banana fig pear</textList>

<searchSorted type="text" target="cherry">$words</searchSorted>   <!-- 3, correct -->
<searchSorted target="cherry">$words</searchSorted>               <!-- 1, silently wrong -->

<indexOf type="text" target="fig">$words</indexOf>                <!-- 3, correct -->
<indexOf target="fig">$words</indexOf>                            <!-- 0 -->

The last line reports indexOf did not find NaN among its values — the target was parsed as a number, became NaN, and matched nothing. The NaN in that message is the only visible trace of the cause. The <searchSorted> line gives no diagnostic at all: it returns 1, a perfectly plausible position.

The type an author needs is not always the type attribute

For the index operators the target has to match the type of the list entries, which is usually decided by the children — a referenced <textList>, say — and not by a type attribute at all. type exists to say how bare string children should be read; it happens to double as the target's type because ComponentWithSelectableType falls back to the parent's type. Those two jobs coincide often enough to be confusing and diverge often enough to be wrong.

There is no way to look up the resolved type

Of the nine components, only <substitute> exposes a public type property. The other eight have a type attribute but no readable property, so an author cannot inspect what was chosen:

Component type attribute type property
<substitute> yes yes
<sequence>, <selectFromSequence>, <repeatForSequence>, <animateFromSequence>, <slider>, <updateValue>, <indexOf>, <searchSorted> yes no

Most of these declare type with createPrimitiveOfType: "string" and no public: true, which is why it never becomes a property.

Possible directions

Not a proposal — the right fix probably combines several of these:

  1. Render a meaningful label in the docs. Map _componentWithSelectableType to something like number, math, text, or boolean — follows this component's type instead of leaking the internal name. Cheapest fix, and it addresses the reported symptom.

  2. Make type readable. Mark the type state variable public on the components that have one, so $seq.type works and matches <substitute>. Note the docs would then also need the attribute to explain what it governs.

  3. Infer the type from the entries where the entries define it. For the index operators, deriving the target's type from the children would make <indexOf target="fig">$words</indexOf> simply work. This is the part that goes beyond wording.

  4. Diagnose the mismatch. Where inference is not wanted, warn when a target parses to NaN against a list that is not numeric — the case above is currently silent for <searchSorted>.

  5. Declare the valid values of type, so autocomplete offers them. Five components accept a type but declare no values for it, so the editor can suggest nothing and the docs show no value table. The set they accept is already known — the shared sugar warns (doenet-w0014) on anything outside math, text, number, boolean — it is simply not declared.

    type declares its values type declares nothing
    <sequence>, <selectFromSequence>, <repeatForSequence>, <animateFromSequence> (number, math, letters); <slider> (number, text); <substitute> (math, text); <updateValue> (math, number, boolean, text) <sort>, <shuffle>, <sortIndices>, <indexOf>, <searchSorted>

    The ones that declare values use createComponentOfType with validValues; the ones that do not use a bare createPrimitiveOfType: "string". packages/lsp-tools/src/auto-completer/index.ts reads a.values for attribute-value completion, so declaring them is what makes the editor offer them. This is the smallest and most independent item here — it needs no decision about how the selectable type is resolved.

    Done for the five in Add list operators: cumulative scans and list indices #1824 (<sort>, <shuffle>, <sortIndices>, <indexOf>, <searchSorted>, plus <argMin> and <argMax>), which all now declare number, math, text and boolean. <slider>, <substitute>, <updateValue> and the sequence family already declared theirs, so this direction is complete unless a component is found that still does not.

  6. Give the schema a way to express it. An attribute whose type is "the resolved type of this component" is a real concept the schema cannot currently state; several of the above become easier if it can.

Scope

Nine components, the docs type-rendering pipeline, and possibly schema generation. Deliberately not addressed in #1824, which introduced <indexOf> and <searchSorted>: their target matches what <sequence> and the others already do, and changing that convention belongs in one change across all of them rather than for two new components alone.


🤖 Generated with Claude Code

https://claude.ai/code/session_01JMbAScbaa3RtikSh9C3qam

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:coreInvolving the DoenetML corearea:documentationImprovements or additions to documentationenhancementNew feature or requestneeds-discussionNeeds input from community before proceeding

    Type

    No type

    Fields

    Priority

    None yet

    Effort

    None yet

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions