Skip to content

Commit

Permalink
feat(form): add widget override for number separator
Browse files Browse the repository at this point in the history
  • Loading branch information
anehx committed Mar 4, 2024
1 parent ebcd822 commit 69419b4
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 9 deletions.
11 changes: 11 additions & 0 deletions packages/-ember-caluma/mirage/scenarios/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ export default function (server) {
maxValue: null,
hintText: null,
});
server.create("question", {
slug: "age-in-seconds",
label: "How many seconds is that?",
infoText: null,
formIds: [form.id],
type: "INTEGER",
minValue: 0,
maxValue: null,
hintText: null,
meta: { widgetOverride: "cf-field/input/number-separator" },
});
server.create("question", {
slug: "height",
label: "How tall are you in meters?",
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 @@ -129,6 +129,7 @@ caluma:
widgetOverrides:
powerselect: "Power Select"
hidden: "Versteckt"
number-separator: "Zahlentrennzeichen"

not-found: "Keine Frage mit dem Slug '{slug}' gefunden"

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 @@ -129,6 +129,7 @@ caluma:
widgetOverrides:
powerselect: "Power Select"
hidden: "Hidden"
number-separator: "Number separator"

not-found: "No question with slug '{slug}' found"

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 @@ -129,6 +129,7 @@ caluma:
widgetOverrides:
powerselect: "Power Select"
hidden: "Caché"
number-separator: "Séparateur de chiffres"

not-found: "Aucune question trouvée avec le slug '{slug}'"

Expand Down
9 changes: 9 additions & 0 deletions packages/form/addon/components/cf-field-value.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@
@onClick={{fn this.download file.id}}
/>
{{/each}}
{{else if
(and
@field.answer.value
(eq
@field.question.raw.meta.widgetOverride "cf-field/input/number-separator"
)
)
}}
{{format-number @field.answer.value maximumFractionDigits=20}}
{{else}}
{{@field.answer.value}}
{{/if}}
12 changes: 12 additions & 0 deletions packages/form/addon/components/cf-field/input/number-separator.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<input
type="text"
class="uk-input
{{if @field.isInvalid 'uk-form-danger'}}
{{if this.disabled 'uk-disabled'}}"
name={{@field.pk}}
id={{@field.pk}}
value={{this.displayValue}}
placeholder={{@field.question.raw.placeholder}}
readonly={{this.disabled}}
{{on "input" this.input}}
/>
46 changes: 46 additions & 0 deletions packages/form/addon/components/cf-field/input/number-separator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import Component from "@glimmer/component";
import { cached } from "tracked-toolbox";

export default class CfFieldInputNumberSeparatorComponent extends Component {
@service intl;

get disabled() {
return this.args.disabled || this.args.field?.question.isCalculated;
}

get displayValue() {
if (!this.args.field.value) {
return "";
}

return this.intl.formatNumber(this.args.field.value, {
maximumFractionDigits: 20,
});
}

@cached
get thousandSeparator() {
return this.intl.formatNumber(11111).replace(/\p{Number}/gu, "");
}

@cached
get decimalSeparator() {
return this.intl.formatNumber(1.1).replace(/\p{Number}/gu, "");
}

@action
input({ target: { value } }) {
// We need to remove the thousand separator and replace the decimal
// separator with a dot in order to parse it into a number. Which character
// those are is determined per locale in the getters above.
const serialized = Number(
value
.replace(new RegExp(`\\${this.thousandSeparator}`, "g"), "")
.replace(new RegExp(`\\${this.decimalSeparator}`), "."),
);

this.args.onSave(isNaN(serialized) ? null : serialized);
}
}
30 changes: 22 additions & 8 deletions packages/form/addon/instance-initializers/form-widget-overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { setOwner } from "@ember/application";
import { inject as service } from "@ember/service";

import HiddenComponent from "@projectcaluma/ember-form/components/cf-field/input/hidden";
import NumberSeparatorComponent from "@projectcaluma/ember-form/components/cf-field/input/number-separator";
import PowerSelectComponent from "@projectcaluma/ember-form/components/cf-field/input/powerselect";

class HiddenOverride {
Expand Down Expand Up @@ -34,17 +35,30 @@ class PowerSelectOverride {
];
}

export function initialize(appInstance) {
const options = appInstance.lookup("service:caluma-options");
class NumberSeparatorOverride {
@service intl;

const hiddenOverride = new HiddenOverride();
const powerSelectOverride = new PowerSelectOverride();
get label() {
return this.intl.t(
"caluma.form-builder.question.widgetOverrides.number-separator",
);
}

component = "cf-field/input/number-separator";
componentClass = NumberSeparatorComponent;
types = ["IntegerQuestion", "FloatQuestion", "CalculatedFloatQuestion"];
}

setOwner(hiddenOverride, appInstance);
setOwner(powerSelectOverride, appInstance);
export function initialize(appInstance) {
const options = appInstance.lookup("service:caluma-options");

options.registerComponentOverride(hiddenOverride);
options.registerComponentOverride(powerSelectOverride);
[HiddenOverride, PowerSelectOverride, NumberSeparatorOverride].forEach(
(cls) => {
const override = new cls();
setOwner(override, appInstance);
options.registerComponentOverride(override);
},
);
}

export default {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "@projectcaluma/ember-form/components/cf-field/input/number-separator";
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { setupMirage } from "ember-cli-mirage/test-support";
import { setupIntl } from "ember-intl/test-support";
import { setLocale, setupIntl } from "ember-intl/test-support";
import { module, test } from "qunit";

import { setupRenderingTest } from "dummy/tests/helpers";
Expand Down Expand Up @@ -133,4 +133,25 @@ module("Integration | Component | cf-field-value", function (hooks) {

assert.dom(this.element).hasText(file.name);
});

test("it numbers using the number-separator widget override", async function (assert) {
setLocale(["de-ch", "de"]);

this.field = {
questionType: "FloatQuestion",
question: {
raw: {
__typename: "FloatQuestion",
meta: { widgetOverride: "cf-field/input/number-separator" },
},
},
answer: {
value: 1111111.111111,
},
};

await render(hbs`<CfFieldValue @field={{this.field}} />`);

assert.dom(this.element).hasText("1’111’111.111111");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { fillIn, render } from "@ember/test-helpers";
import { tracked } from "@glimmer/tracking";
import { hbs } from "ember-cli-htmlbars";
import { setLocale, setupIntl } from "ember-intl/test-support";
import { module, test } from "qunit";

import { setupRenderingTest } from "dummy/tests/helpers";

module(
"Integration | Component | cf-field/input/number-separator",
function (hooks) {
setupRenderingTest(hooks);
setupIntl(hooks);

hooks.beforeEach(function () {
setLocale(["de-ch", "de"]);

this.field = new (class {
questionType = "IntegerQuestion";
question = { isCalculated: false };
@tracked value = null;
})();

this.save = (value) => {
this.field.value = value;
};
});

test("it converts integers to formatted strings and saves them properly", async function (assert) {
await render(
hbs`<CfField::Input::NumberSeparator @field={{this.field}} @onSave={{this.save}} />`,
);

await fillIn("input", "1234");

assert.strictEqual(this.field.value, 1234);
assert.dom("input").hasValue("1’234");
});

test("it converts floats to formatted strings and saves them properly", async function (assert) {
this.field.questionType = "FloatQuestion";

await render(
hbs`<CfField::Input::NumberSeparator @field={{this.field}} @onSave={{this.save}} />`,
);

await fillIn("input", "1234.123");

assert.strictEqual(this.field.value, 1234.123);
assert.dom("input").hasValue("1’234.123");
});

test("it displays calculated floats properly", async function (assert) {
this.field.questionType = "CalculatedFloatQuestion";
this.field.question.isCalculated = true;
this.field.value = 1234.123;

await render(
hbs`<CfField::Input::NumberSeparator @field={{this.field}} />`,
);

assert.dom("input").hasAttribute("readonly");
assert.dom("input").hasClass("uk-disabled");
assert.dom("input").hasValue("1’234.123");
});

test("it works with other locales", async function (assert) {
setLocale(["en-us", "en"]);

await render(
hbs`<CfField::Input::NumberSeparator @field={{this.field}} @onSave={{this.save}} />`,
);

await fillIn("input", "1234.123");

assert.strictEqual(this.field.value, 1234.123);
assert.dom("input").hasValue("1,234.123");
});
},
);

0 comments on commit 69419b4

Please sign in to comment.