Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: enable JEXLs for options in choice and multiple choice questions #2767

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<li class="cfb-option-row" data-test-row={{concat "option-" (add @i 1)}}>
<ValidatedForm @model={{@row}} as |f|>
<div uk-grid class="uk-grid-small uk-flex uk-flex-top" id={{@row.slug}}>
<div class="uk-width-auto uk-flex uk-flex-middle">
{{#if @canReorder}}
<span
data-test-sort-handle
uk-icon="menu"
class="uk-sortable-handle uk-margin-small-right"
role="button"
></span>
{{/if}}
{{#if (is-empty @row.id)}}
<button
data-test-delete-row
type="button"
class="uk-icon-button"
uk-icon="trash"
title={{t "caluma.form-builder.options.delete"}}
{{on "click" (fn @deleteRow @row)}}
>
</button>
{{else}}
<button
data-test-archive-row
type="button"
class="uk-icon-button"
uk-icon={{if @row.isArchived "refresh" "close"}}
title={{t
(concat
"caluma.form-builder.options."
(if @row.isArchived "restore" "archive")
)
}}
{{on
"click"
(fn (changeset-set @row "isArchived") (not @row.isArchived))
}}
>
</button>
{{/if}}
</div>
<div class="uk-width-expand">
<f.input
@name="label"
@inputName={{concat "option-" (add @i 1) "-label"}}
@required={{true}}
@disabled={{@row.isArchived}}
@submitted={{@submitted}}
@on-update={{@updateLabel}}
/>
</div>
<div class="uk-width-1-4">
<f.input
@name="slug"
@inputName={{concat "option-" (add @i 1) "-slug"}}
@required={{true}}
@disabled={{not (is-empty @row.id)}}
@submitted={{@submitted}}
@renderComponent={{component
"cfb-slug-input"
hidePrefix=true
[email protected]
onUnlink=(fn (mut @row.slugUnlinked) true)
}}
/>
</div>
<div class="uk-width-auto">
<button
data-test-toggle-jexl
type="button"
class="uk-icon-button"
uk-icon={{if this.showJexl "triangle-up" "triangle-down"}}
title={{t "caluma.form-builder.options.show-jexl"}}
{{on "click" (fn (mut this.showJexl) (not this.showJexl))}}
/>
</div>
{{#if this.showJexl}}
<div class="uk-width-4-4">
<f.input
@label={{t "caluma.form-builder.question.isHidden"}}
@name="isHidden"
@renderComponent={{component "cfb-code-editor" language="jexl"}}
/>
</div>
{{/if}}
</div>
</ValidatedForm>
</li>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";

export default class CfbFormEditorQuestionOption extends Component {
@tracked showJexl = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,11 @@ export default class CfbFormEditorQuestion extends Component {
(changeset.get("options") || [])
.filter((option) => option.get("isDirty"))
.map(async (option) => {
const { label, slug, isArchived } = option;
const { label, slug, isArchived, isHidden } = option;

await this.apollo.mutate({
mutation: saveOptionMutation,
variables: { input: { label, slug, isArchived } },
variables: { input: { label, slug, isArchived, isHidden } },
});
}),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,80 +8,14 @@
class="uk-list uk-list-divider uk-form-controls uk-margin-small-top"
>
{{#each @value as |row i|}}
<li class="cfb-option-row" data-test-row={{concat "option-" (add i 1)}}>
<ValidatedForm @model={{row}} as |f|>
<div
uk-grid
class="uk-grid-small uk-flex uk-flex-top"
id={{row.slug}}
>
<div class="uk-width-auto uk-flex uk-flex-middle">
{{#if this.canReorder}}
<span
data-test-sort-handle
uk-icon="menu"
class="uk-sortable-handle uk-margin-small-right"
role="button"
></span>
{{/if}}
{{#if (is-empty row.id)}}
<button
data-test-delete-row
type="button"
class="uk-icon-button"
uk-icon="trash"
title={{t "caluma.form-builder.options.delete"}}
{{on "click" (fn this.deleteRow row)}}
>
</button>
{{else}}
<button
data-test-archive-row
type="button"
class="uk-icon-button"
uk-icon={{if row.isArchived "refresh" "close"}}
title={{t
(concat
"caluma.form-builder.options."
(if row.isArchived "restore" "archive")
)
}}
{{on
"click"
(fn (changeset-set row "isArchived") (not row.isArchived))
}}
>
</button>
{{/if}}
</div>
<div class="uk-width-expand">
<f.input
@name="label"
@inputName={{concat "option-" (add i 1) "-label"}}
@required={{true}}
@disabled={{row.isArchived}}
@submitted={{@submitted}}
@on-update={{this.updateLabel}}
/>
</div>
<div class="uk-width-1-4">
<f.input
@name="slug"
@inputName={{concat "option-" (add i 1) "-slug"}}
@required={{true}}
@disabled={{not (is-empty row.id)}}
@submitted={{@submitted}}
@renderComponent={{component
"cfb-slug-input"
hidePrefix=true
[email protected]
onUnlink=(fn (mut row.slugUnlinked) true)
}}
/>
</div>
</div>
</ValidatedForm>
</li>
<CfbFormEditor::QuestionOption
@i={{i}}
@row={{row}}
@model={{@model}}
@canReorder={{this.canReorder}}
@deleteRow={{fn this.deleteRow row}}
@updateLabel={{this.updateLabel}}
/>
{{/each}}
<li class="uk-text-center">
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mutation SaveOption($input: SaveOptionInput!) {
id
label
slug
isHidden
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ query FormEditorQuestion($slug: String!) {
label
slug
isArchived
isHidden
}
}
}
Expand All @@ -93,6 +94,7 @@ query FormEditorQuestion($slug: String!) {
label
slug
isArchived
isHidden
}
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/form-builder/translations/de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ caluma:
delete: "Option löschen"
archive: "Option archivieren (verstecken)"
restore: "Option wiederherstellen"
show-jexl: "JEXL anzeigen"

notification:
form:
Expand Down
1 change: 1 addition & 0 deletions packages/form-builder/translations/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ caluma:
delete: "Delete option"
archive: "Archive (hide) option"
restore: "Restore option"
show-jexl: "Show JEXL"

notification:
form:
Expand Down
1 change: 1 addition & 0 deletions packages/form-builder/translations/fr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ caluma:
delete: "Supprimer l'option"
archive: "Archiver (masquer) l'option"
restore: "Restaurer l'option"
show-jexl: "Afficher JEXL"

notification:
form:
Expand Down
2 changes: 2 additions & 0 deletions packages/form/addon/gql/fragments/field.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ fragment SimpleQuestion on Question {
slug
label
isArchived
isHidden
}
}
}
Expand All @@ -89,6 +90,7 @@ fragment SimpleQuestion on Question {
slug
label
isArchived
isHidden
}
}
}
Expand Down
41 changes: 37 additions & 4 deletions packages/form/addon/lib/field.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,10 @@ export default class Field extends Base {
* - DynamicMultipleChoiceQuestion
*
* This will also return the disabled state of the option. An option can only
* be disabled, if it is an old value used in a dynamic question.
* be disabled in any of the following cases:
* - it is a dynamic option which is no longer available
* - the option has been removed from the form using the form builder
* - the option is no longer visible due to its JEXL
*
* @property {null|Object[]} options
*/
Expand All @@ -352,9 +355,39 @@ export default class Field extends Base {
const selected =
(this.question.isMultipleChoice ? this.value : [this.value]) || [];

const options = this.question.options.filter(
(option) => !option.disabled || selected.includes(option.slug),
);
const options = this.question.options
.filter((option) => !option.disabled || selected.includes(option.slug))
.map((option) => {
if (!option.isHidden) {
return option;
}

try {
const isHidden = this.document.jexl.evalSync(
option.isHidden,
this.jexlContext,
);

if (selected.includes(option.slug) && isHidden) {
return {
...option,
_isHidden: isHidden,
disabled: true,
};
}
return {
...option,
_isHidden: isHidden,
};
} catch (error) {
throw new Error(
`Error while evaluating \`isHidden\` expression on Option\`${option.slug}\`: ${error.message}`,
);
}
})
.filter(
({ _isHidden, disabled }) => !_isHidden || (_isHidden && disabled),
);

const hasUnknownValue = !selected.every((slug) =>
options.find((option) => option.slug === slug),
Expand Down
13 changes: 8 additions & 5 deletions packages/form/addon/lib/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,14 @@ export default class Question extends Base {
const key = camelize(this.raw.__typename.replace(/Question$/, "Options"));
const raw = this.isDynamic ? this[key] : this.raw[key];

return (raw?.edges || []).map(({ node: { label, slug, isArchived } }) => ({
label,
slug,
disabled: isArchived || false,
}));
return (raw?.edges || []).map(
({ node: { label, slug, isArchived, isHidden } }) => ({
label,
slug,
disabled: isArchived || false,
isHidden,
}),
);
}

/**
Expand Down
Loading
Loading