FIX: ensures empty fields don't store an empty array (#33274)

This is a follow-up to
299d28cb73
to apply it to more fields.
This commit is contained in:
Joffrey JAFFEUX
2025-06-19 20:46:48 +02:00
committed by GitHub
parent 35d9688aa3
commit c9425c93cc
12 changed files with 189 additions and 14 deletions

View File

@ -1,5 +1,6 @@
import { fn, hash } from "@ember/helper";
import { hash } from "@ember/helper";
import { action } from "@ember/object";
import { isBlank } from "@ember/utils";
import Category from "discourse/models/category";
import CategorySelector from "select-kit/components/category-selector";
import BaseField from "./da-base-field";
@ -7,6 +8,16 @@ import DAFieldDescription from "./da-field-description";
import DAFieldLabel from "./da-field-label";
export default class CategoriesField extends BaseField {
@action
onChangeCategories(categories) {
if (isBlank(categories)) {
this.mutValue(undefined);
return;
}
this.mutValue(categories.mapBy("id"));
}
<template>
{{! template-lint-disable no-redundant-fn }}
<section class="field categories-field">
@ -16,7 +27,7 @@ export default class CategoriesField extends BaseField {
<div class="controls">
<CategorySelector
@categories={{this.categories}}
@onChange={{fn this.onChangeCategories}}
@onChange={{this.onChangeCategories}}
@options={{hash clearable=true disabled=@field.isDisabled}}
/>
@ -30,9 +41,4 @@ export default class CategoriesField extends BaseField {
const ids = this.args.field?.metadata?.value || [];
return ids.map((id) => Category.findById(id)).filter(Boolean);
}
@action
onChangeCategories(categories) {
this.mutValue(categories.mapBy("id"));
}
}

View File

@ -1,4 +1,6 @@
import { hash } from "@ember/helper";
import { action } from "@ember/object";
import { isBlank } from "@ember/utils";
import { i18n } from "discourse-i18n";
import ComboBox from "select-kit/components/combo-box";
import MultiSelect from "select-kit/components/multi-select";
@ -7,6 +9,15 @@ import DAFieldDescription from "./da-field-description";
import DAFieldLabel from "./da-field-label";
export default class ChoicesField extends BaseField {
@action
onChangeChoices(choices) {
if (isBlank(choices)) {
choices = undefined;
}
this.mutValue(choices);
}
<template>
<div class="field control-group">
<DAFieldLabel @label={{@label}} @field={{@field}} />
@ -16,7 +27,7 @@ export default class ChoicesField extends BaseField {
<MultiSelect
@value={{@field.metadata.value}}
@content={{this.replacedContent}}
@onChange={{this.mutValue}}
@onChange={{this.onChangeChoices}}
@options={{hash
allowAny=false
clearable=true

View File

@ -1,6 +1,7 @@
import { tracked } from "@glimmer/tracking";
import { hash } from "@ember/helper";
import { action } from "@ember/object";
import { isBlank } from "@ember/utils";
import Group from "discourse/models/group";
import GroupChooser from "select-kit/components/group-chooser";
import BaseField from "./da-base-field";
@ -30,6 +31,10 @@ export default class GroupsField extends BaseField {
@action
setGroupField(groupIds) {
if (isBlank(groupIds)) {
groupIds = undefined;
}
this.mutValue(groupIds);
}
@ -41,7 +46,7 @@ export default class GroupsField extends BaseField {
<div class="controls">
<GroupChooser
@content={{this.allGroups}}
@value={{@field.metadata.value}}
@value={{readonly @field.metadata.value}}
@labelProperty="name"
@onChange={{this.setGroupField}}
@options={{hash maximum=this.maximum disabled=@field.isDisabled}}

View File

@ -10,7 +10,7 @@ export default class TagsField extends BaseField {
@action
onChangeTags(tags) {
if (isBlank(tags)) {
tags = undefined; // avoids storing [] as value which can complicate logic in the backend
tags = undefined;
}
this.mutValue(tags);

View File

@ -1,4 +1,6 @@
import { action } from "@ember/object";
import { service } from "@ember/service";
import { isBlank } from "@ember/utils";
import MultiSelect from "select-kit/components/multi-select";
import BaseField from "./da-base-field";
import DAFieldDescription from "./da-field-description";
@ -7,6 +9,15 @@ import DAFieldLabel from "./da-field-label";
export default class TrustLevelsField extends BaseField {
@service site;
@action
onChangeTrustLevels(value) {
if (isBlank(value)) {
value = undefined;
}
this.mutValue(value);
}
<template>
<section class="field category-field">
<div class="control-group">
@ -16,7 +27,7 @@ export default class TrustLevelsField extends BaseField {
<MultiSelect
@value={{@field.metadata.value}}
@content={{this.site.trustLevels}}
@onChange={{this.mutValue}}
@onChange={{this.onChangeTrustLevels}}
/>
<DAFieldDescription @description={{@description}} />

View File

@ -1,10 +1,21 @@
import { hash } from "@ember/helper";
import { action } from "@ember/object";
import { isBlank } from "@ember/utils";
import UserChooser from "select-kit/components/user-chooser";
import BaseField from "./da-base-field";
import DAFieldDescription from "./da-field-description";
import DAFieldLabel from "./da-field-label";
export default class UsersField extends BaseField {
@action
onChangeUsers(users) {
if (isBlank(users)) {
users = undefined;
}
this.mutValue(users);
}
<template>
<section class="field users-field">
<div class="control-group">
@ -13,7 +24,7 @@ export default class UsersField extends BaseField {
<div class="controls">
<UserChooser
@value={{@field.metadata.value}}
@onChange={{this.mutValue}}
@onChange={{this.onChangeUsers}}
@options={{hash
excludeCurrentUser=false
disabled=@field.isDisabled

View File

@ -35,4 +35,30 @@ module("Integration | Component | da-categories-field", function (hooks) {
assert.deepEqual(this.field.metadata.value, [6, 8]);
});
test("empty", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({
component: "categories",
});
await render(
<template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
);
await selectKit().expand();
await selectKit().selectRowByValue(6);
assert.deepEqual(this.field.metadata.value, [6]);
await selectKit().deselectItemByValue(6);
assert.strictEqual(this.field.metadata.value, undefined);
});
});

View File

@ -35,4 +35,31 @@ module("Integration | Component | da-choices-field", function (hooks) {
assert.strictEqual(this.field.metadata.value, 1);
});
test("empty multiselect", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({
component: "choices",
extra: { multiselect: true, content: [{ name: "One", id: 1 }] },
});
await render(
<template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
);
await selectKit().expand();
await selectKit().selectRowByValue(1);
assert.deepEqual(this.field.metadata.value, [1]);
await selectKit().deselectItemByValue(1);
assert.strictEqual(this.field.metadata.value, undefined);
});
});

View File

@ -55,7 +55,7 @@ module("Integration | Component | da-groups-field", function (hooks) {
assert.deepEqual(this.field.metadata.value, [1]);
});
test("supports a maxmimum value", async function (assert) {
test("supports a maximum value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({
@ -82,4 +82,30 @@ module("Integration | Component | da-groups-field", function (hooks) {
assert.deepEqual(this.field.metadata.value, [2]);
});
test("empty", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({
component: "groups",
});
await render(
<template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
);
await selectKit().expand();
await selectKit().selectRowByValue(1);
assert.deepEqual(this.field.metadata.value, [1]);
await selectKit().deselectItemByValue(1);
assert.strictEqual(this.field.metadata.value, undefined);
});
});

View File

@ -57,6 +57,6 @@ module("Integration | Component | da-tags-field", function (hooks) {
await selectKit().deselectItemByValue("monkey");
assert.deepEqual(this.field.metadata.value, undefined);
assert.strictEqual(this.field.metadata.value, undefined);
});
});

View File

@ -35,4 +35,29 @@ module("Integration | Component | da-trust-levels-field", function (hooks) {
assert.deepEqual(this.field.metadata.value, [1, 2]);
});
test("empty", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({
component: "trust-levels",
});
await render(
<template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
);
await selectKit().expand();
await selectKit().selectRowByValue(1);
assert.deepEqual(this.field.metadata.value, [1]);
await selectKit().deselectItemByValue(1);
assert.strictEqual(this.field.metadata.value, undefined);
});
});

View File

@ -78,4 +78,31 @@ module("Integration | Component | da-users-field", function (hooks) {
assert.deepEqual(this.field.metadata.value, ["j.jaffeux@example.com"]);
});
test("empty", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({
component: "users",
});
await render(
<template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
);
await selectKit().expand();
await selectKit().fillInFilter("sam");
await selectKit().selectRowByValue("sam");
assert.deepEqual(this.field.metadata.value, ["sam"]);
await selectKit().deselectItemByValue("sam");
assert.strictEqual(this.field.metadata.value, undefined);
});
});