Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Dynamic options for checkbox/radio/selector #303

Merged
merged 7 commits into from
Mar 10, 2023
Merged

Dynamic options for checkbox/radio/selector #303

merged 7 commits into from
Mar 10, 2023

Conversation

ianroberts
Copy link
Member

Implementing a mechanism to take checkbox/radio/selector options from the document as well as from the static project configuration, to allow for things like entity disambiguation tasks where the list of possible options varies from document to document.

The initial implementation is based on a new type of entry in the options array, so as well as

{"value": "some-value", "label": "Some value"}

we now support

{"fromDocument": "someProperty"}

which in turn expects someProperty in the document JSON to have the same form as the original options configuration itself (either an array of value/label objects, or a dictionary mapping values to labels). For convenience I've also added support for an array of plain strings, where a string "X" is treated as if it were {"value": "X", "label": "X"}.

Closes #289

@ianroberts
Copy link
Member Author

I've created this as a draft so I can ask for feedback on the idea in principle before I attempt to add the "delimited" format that I've mentioned in #289.

@ianroberts ianroberts requested a review from twinkarma March 3, 2023 12:44
@github-actions
Copy link

github-actions bot commented Mar 3, 2023

Jest Coverage

File % Stmts % Branch % Funcs % Lines Uncovered Line #s
All files 80 77.37 74.19 79.92
File % Stmts % Branch % Funcs % Lines Uncovered Line #s
All files 80 77.37 74.19 79.92
_src/components 77.14 77.35 85.71 77.14
_ AnnotationRenderer.vue 76.23 77.35 84 76.23 126,141-142,196,219,240,247-261,269,275-285,319
_ MarkdownEditor.vue 100 100 100 100
_ MarkdownRenderer.vue 100 100 100 100
_src/components/annotation 80.95 86.66 82.35 80.95
_ CheckboxInput.vue 85.71 100 100 85.71 19
_ HtmlDisplay.vue 80 100 100 80 26-30
_ InputErrorDisplay.vue 100 0 100 100 19-25
_ RadioInput.vue 85.71 100 100 85.71 19
_ SelectorInput.vue 71.42 100 66.66 71.42 20,27
_ TextInput.vue 75 100 66.66 75 26
_ TextareaInput.vue 66.66 100 50 66.66 20
_src/enum 100 100 100 100
_ DocumentTypes.js 100 100 100 100
_src/jrpc 90.32 83.33 75 90.32
_ index.js 90.32 83.33 75 90.32 29,38-39
_src/utils 77.77 74.6 38.46 77.46
_ annotations.js 96.55 81.25 100 96.42 35
_ dict.js 100 100 100 100
_ index.js 55.88 54.54 11.11 55.88 9-14,28-93
_tests/unit 100 100 100 100
_ globalVue.js 100 100 100 100

optionsList.push(...generateBVOptions(propertyValue));
} else if (option !== null && typeof option !== "undefined") {
// single option
if (typeof option === 'string') {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically this bit also allows the list-of-strings format within the project configuration directly, i.e.

{"type":"checkbox", "name":"foo", "options":["option 1", "option 2"]}

but this should be rejected by the schema-driven JSON editor.

@twinkarma
Copy link
Collaborator

twinkarma commented Mar 6, 2023

A note from our recent discussion, from my understanding the behaviour of fromDocument is :

{"fromDocument": "someProperty"}

if someProperty is:

  • An object/dictionary - works exactly the same as default behavior
           { // The options can be specified as a dictionary, ordering is not guaranteed
               "value1": "Text to show user 1",
               "value2": "Text to show user 2",
               "value3": "Text to show user 3"
           }
    
  • An array of objects - works the same as the default behavior and expects the following structure:
    [ // The options that the user is able to select from
         {"value": "value1", "label": "Text to show user 1"},
         {"value": "value2", "label": "Text to show user 2"},
         {"value": "value3", "label": "Text to show user 3"}
     ], 
    
  • An array of string - The string is used for both label and value
  • A single string (support for CSV document imports so that we don't need to have multiple columns to define the options). We will look for a delimiter (; by default) to separate the options e.g. value1;value2
    • You can define your own delimiter and separator in the fromDocument option e.g.
      {"fromDocument:"someProperty", "separator":":", "delimiter":";"} to do a key value split of the string (separator is blank/disabled by default).

@ianroberts
Copy link
Member Author

Yes, the first three (dict, array-of-object, array-of-string) already work, the single string with delimiters is still to be implemented.

@twinkarma
Copy link
Collaborator

Not sure what the default combination of separators & delimiters makes the most sense though. Should we be detecting only delimiters ; by default or should we be doing separator & delimiters (: and ;)?

Thinking about it, I'd imagine in most cases people would want to do the second case more often?

@ianroberts
Copy link
Member Author

I've implemented the delimited string option now, with the default delimiters being ; between options and = between the value and label of a single option, i.e.

apple=Apples; orange=Oranges; kiwi=Kiwi fruit

The between-options delimiter can be overridden by specifying separator and the value-label separator by valueLabelSeparator (slightly clunky name but I didn't want to use terminology like "key-value" as that implies the "value" is the right hand half of each pair when in Teamware terms it's the left half).

{"fromDocument": "options", "separator": "~~", "valueLabelSeparator": "::"}
apple::Apples ~~ orange::Oranges ~~ kiwi=Kiwi fruit

(Note I've also made it .trim() every sub-string after splitting, so spaces around any delimiter are ignored)

@ianroberts ianroberts marked this pull request as ready for review March 8, 2023 23:31
Copy link
Collaborator

@twinkarma twinkarma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried this out and it's working for me!

Can you add an example document.json and project configuration.json to the examples folder?

Here's an example of one I made to test the feature based on your documentation:

[
  {
    "id": "1",
    "text": "Testing options specified with array of dictionary",
    "options": [
      {"value": "val1","label":  "Object label 1"},
      {"value": "val2","label":  "Object label 2"},
      {"value": "val3","label":  "Object label 3"}
    ]

  },
  {
    "id": "2",
    "text": "Testing options specified with array of dictionary",
    "options": {
      "val1": "Key value label 1",
      "val2": "Key value label 2",
      "val3": "Key value label 3"
    }
  },
  {
    "id": "3",
    "text": "Testing options specified with array of string",
    "options": [
      "String array label 1", "String array label 2", "String array label 3"
    ]
  },
  {
    "id": "4",
    "text": "Testing options specified with a single string, separated using special characters (= and ; by default)",
    "options": "val1=Separator label 1;val2=Separator label  2;val3=Separator label  3"
  },
  {
    "id": "5",
    "text": "Testing options specified with a custom separator (:: and ~~ in this case)",
    "options": "val1::Custom separator label 1~~val2::Custom separator label 2~~val3::Custom separator label 3"
  }
]

The structured array or object forms of fromDocument options work well when documents are supplied in JSON but are tricky to map effectively when uploading documents as CSV.  We now also support the case where the property pointed to by fromDocument is a single string.  By default this is assumed to be a semicolon-separated list of options, where each option is of the form value=label, i.e.

    {"options":"orange=Orange; kiwi=Kiwi fruit"}

is treated as if it were

    {"options":[
      {"value": "orange", "label": "Orange},
      {"value": "kiwi", "label": "Kiwi fruit"}
    ]}

The ';' and '=' separators are configurable by properties alongside the "fromDocument", and can be any string (not just single characters), e.g.

    {"fromDocument": "options", "separator":"##", "valueLabelSeparator": ":"}

with document

    {"options":"option1: Option 1##option2: Option 2"}
- project config that has a radio button with a "fromDocument" declaration plus one static option
- a set of four sample documents that each defines three dynamic options in one of the valid formats
  - array of {value/label} objects
  - plain dictionary
  - array of strings
  - single string with val1=Label1;val2=Label2

and a test that verifies that all four documents render four options each (the three from the document plus the one from the config)
…docs, apart from the one section where we introduce the dict format as an alternative.
@ianroberts
Copy link
Member Author

I've added the examples you requested, and also improved the documentation more generally so we consistently use the array style (that preserves order) rather than dict style (that doesn't) for all checkbox & radio samples except the section that specifically introduces the dict style as an alternative.

@ianroberts ianroberts requested a review from twinkarma March 10, 2023 11:08
Copy link
Collaborator

@twinkarma twinkarma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants