Skip to content

Commit 779f88d

Browse files
committed
fix: c-select multiple chips are no longer removable when the input is non-interactive.
1 parent e319d48 commit 779f88d

File tree

4 files changed

+47
-20
lines changed

4 files changed

+47
-20
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@
4646

4747
## Fixes
4848
- Fix error in codegen when using JS reserved keywords or C# contextual keywords as parameter names.
49-
- Fix c-select not receiving proper disabled styling
5049
- Default search behavior when no SearchAttribute is present no longer splits on spaces. The old behavior would result in multi-word searches failing to match because the default search behavior is StartsWith, not Contains.
5150
- Fixed InvalidOperationException when searching on collection properties with no searchable child properties.
51+
- `c-select` now receives proper disabled styling.
52+
- `c-select` with `autoselect` now works reliably `c-select` in dialogs.
53+
- `c-select` multiple chips are no longer removable when the input is non-interactive.
5254

5355
# 5.3.8
5456

playground/Coalesce.Web.Vue3/src/examples/c-select/styling.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@
7676
</v-col>
7777
</v-row>
7878

79+
<h1>readonly multiple</h1>
80+
<v-row>
81+
<v-col cols="6">
82+
<c-input :model="caseVm" for="caseProducts" readonly variant="outlined" />
83+
</v-col>
84+
</v-row>
85+
7986
<h1>autofocus in v-dialog</h1>
8087
<v-dialog v-model="dialogOpen" max-width="500">
8188
<template #activator="{ props }">

src/coalesce-vue-vuetify3/src/components/input/c-select.spec.tsx

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ describe("CSelect", () => {
5757
(c, i) =>
5858
(!config.params.search ||
5959
c.testName?.startsWith(config.params.search)) &&
60-
(!config.params.pageSize || i < config.params.pageSize)
60+
(!config.params.pageSize || i < config.params.pageSize),
6161
);
6262
return {
6363
wasSuccessful: true,
@@ -67,15 +67,15 @@ describe("CSelect", () => {
6767
pageSize: 10,
6868
totalCount: items.length,
6969
};
70-
})
70+
}),
7171
);
7272

7373
get303Mock = mockEndpoint(
7474
"/Test/get/303",
7575
vitest.fn((config) => ({
7676
wasSuccessful: true,
7777
object: new Test({ testId: 303, testName: "baz 303" }),
78-
}))
78+
})),
7979
);
8080
});
8181

@@ -372,7 +372,7 @@ describe("CSelect", () => {
372372
expect(
373373
//@ts-expect-error We're asserting this prop doesn't have a required rule,
374374
// but our types are so good that this even gets caught by typescript.
375-
model.$metadata.props.referenceNavigationId.rules?.required
375+
model.$metadata.props.referenceNavigationId.rules?.required,
376376
).toBeFalsy();
377377

378378
expect(wrapper.find(".v-field__clearable").exists()).toBeTruthy();
@@ -397,7 +397,7 @@ describe("CSelect", () => {
397397
expect(
398398
// @ts-expect-error We're asserting this prop doesn't have a required rule,
399399
// but our types are so good that this even gets caught by typescript.
400-
methodMeta.params.optionalObject.rules?.required
400+
methodMeta.params.optionalObject.rules?.required,
401401
).toBeFalsy();
402402

403403
expect(wrapper.find(".v-field__clearable").exists()).toBeTruthy();
@@ -449,7 +449,7 @@ describe("CSelect", () => {
449449
describe("validation", () => {
450450
async function assertValidation(
451451
TargetComponent: FunctionalComponent,
452-
message: string
452+
message: string,
453453
) {
454454
const wrapper = mountApp(() => (
455455
<VForm>
@@ -470,7 +470,7 @@ describe("CSelect", () => {
470470
test("pulls validation from fk metadata", async () => {
471471
await assertValidation(
472472
() => <CSelect model={model} for={propName}></CSelect>,
473-
"Single Test is required."
473+
"Single Test is required.",
474474
);
475475
});
476476

@@ -479,11 +479,11 @@ describe("CSelect", () => {
479479
model.$addRule(
480480
"singleTestId",
481481
"required",
482-
(v: any) => !!v || "Custom rule from VM."
482+
(v: any) => !!v || "Custom rule from VM.",
483483
);
484484
await assertValidation(
485485
() => <CSelect model={model} for={propName}></CSelect>,
486-
"Custom rule from VM."
486+
"Custom rule from VM.",
487487
);
488488
});
489489

@@ -496,7 +496,7 @@ describe("CSelect", () => {
496496
rules={[(v: any) => !!v || "Custom rule"]}
497497
></CSelect>
498498
),
499-
"Custom rule"
499+
"Custom rule",
500500
);
501501
});
502502
});
@@ -589,10 +589,10 @@ describe("CSelect", () => {
589589
// Assert: Emits events
590590
expect(onUpdateKey).toHaveBeenCalledWith(101);
591591
expect(onUpdateObject).toHaveBeenCalledWith(
592-
new Test({ testId: 101, testName: "foo 101" })
592+
new Test({ testId: 101, testName: "foo 101" }),
593593
);
594594
expect(onUpdateModel).toHaveBeenCalledWith(
595-
new Test({ testId: 101, testName: "foo 101" })
595+
new Test({ testId: 101, testName: "foo 101" }),
596596
);
597597

598598
// Assert: Menu closes after selection
@@ -657,7 +657,7 @@ describe("CSelect", () => {
657657

658658
// Assert
659659
expect(onUpdate).toHaveBeenCalledWith(
660-
new Test({ testId: 101, testName: "foo 101" })
660+
new Test({ testId: 101, testName: "foo 101" }),
661661
);
662662
});
663663
});
@@ -721,7 +721,7 @@ describe("CSelect", () => {
721721
expect(onSelectionChanged.mock.calls[0][0][0]).toBe(selectedItem);
722722
expect(onSelectionChanged.mock.calls[1][0][0]).toBe(selectedItem);
723723
expect(selectedItem).toBeInstanceOf(TestViewModel);
724-
}
724+
},
725725
);
726726

727727
test("collectionNavigation binding sets inverse nav on select", async () => {
@@ -925,14 +925,14 @@ describe("CSelect", () => {
925925

926926
expect(model.singleTest?.testName).toBe("Create new item");
927927
expect(wrapper.vm.menuOpen).toBeFalsy();
928-
}
928+
},
929929
);
930930

931931
describe.each(["disabled", "readonly"])("%s", (prop) => {
932932
async function assertNonInteractive(
933933
wrapper: VueWrapper<
934934
BetterComponentInstance<typeof CSelect<ComplexModel>>
935-
>
935+
>,
936936
) {
937937
// Clearable is ignored when disabled/readonly
938938
expect(wrapper.find(".v-field__clearable").exists()).toBeFalsy();
@@ -972,6 +972,22 @@ describe("CSelect", () => {
972972

973973
assertNonInteractive(wrapper);
974974
});
975+
976+
test("chips not removable in multiple select", async () => {
977+
const model = new ComplexModelViewModel();
978+
model.tests = [new Test({ testId: 101, testName: "foo 101" })];
979+
const wrapper = mountApp(() => (
980+
<CSelect model={model} for="tests" {...{ [prop]: true }}></CSelect>
981+
)).findComponent(CSelect);
982+
983+
// Assert chip close buttons are not present or not clickable
984+
const closeButtons = wrapper.findAll(".v-chip__close");
985+
expect(closeButtons).toHaveLength(0);
986+
987+
// Ensure selection remains unchanged
988+
expect(model.tests).toHaveLength(1);
989+
expect(model.tests[0].testId).toBe(101);
990+
});
975991
});
976992
});
977993

@@ -999,7 +1015,7 @@ describe("CSelect", () => {
9991015
)).findComponent(CSelect);
10001016
await flushPromises();
10011017
expect(model.singleTestId).toBe(expected);
1002-
}
1018+
},
10031019
);
10041020

10051021
test("create", async () => {
@@ -1045,7 +1061,7 @@ describe("CSelect", () => {
10451061
vitest.fn(() => ({
10461062
wasSuccessful: true,
10471063
object: { testId: 1, testName: "foo" },
1048-
}))
1064+
})),
10491065
);
10501066

10511067
const wrapper = mountApp(() => (

src/coalesce-vue-vuetify3/src/components/input/c-select.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
<v-chip
5252
v-if="effectiveMultiple"
5353
size="small"
54-
:closable="!!canDeselect"
54+
:closable="!!canDeselect && isInteractive"
5555
@click:close="onInput(item)"
5656
>
5757
{{ itemTitle(item) }}
@@ -1000,6 +1000,8 @@ function onInput(
10001000
value: SelectedModelTypeSingle | null,
10011001
dontFocus = false,
10021002
): void {
1003+
if (!isInteractive.value) return;
1004+
10031005
value = value ?? null;
10041006
10051007
if (value === null && !props.canDeselect) {

0 commit comments

Comments
 (0)