([]);
+
+ const handleChange = (event: CustomSelectChangeEvent) => {
+ setValue((event.target.value as unknown) as string[]);
+ if (event.selectionConfirmed) {
+ setConfirmedSelections((event.target.value as unknown) as string[]);
+ }
+ };
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {confirmedSelections.map((cs) => (
+ {cs}
+ ))}
+
+ >
+ );
+};
diff --git a/src/components/select/multi-select/multi-select.component.tsx b/src/components/select/multi-select/multi-select.component.tsx
index 90d967a107..d677ff8027 100644
--- a/src/components/select/multi-select/multi-select.component.tsx
+++ b/src/components/select/multi-select/multi-select.component.tsx
@@ -28,6 +28,7 @@ import useFormSpacing from "../../../hooks/__internal__/useFormSpacing";
import useInputAccessibility from "../../../hooks/__internal__/useInputAccessibility/useInputAccessibility";
import { OptionProps } from "../option";
import { OptionRowProps } from "../option-row";
+import { CustomSelectChangeEvent } from "../simple-select";
let deprecateInputRefWarnTriggered = false;
let deprecateUncontrolledWarnTriggered = false;
@@ -191,16 +192,17 @@ export const MultiSelect = React.forwardRef(
}, [onOpen]);
const createCustomEvent = useCallback(
- (newValue) => {
+ (newValue, selectionConfirmed) => {
const customEvent = {
target: {
...(name && { name }),
...(id && { id }),
value: newValue,
},
+ selectionConfirmed,
};
- return customEvent as React.ChangeEvent;
+ return customEvent as CustomSelectChangeEvent;
},
[name, id]
);
@@ -213,14 +215,15 @@ export const MultiSelect = React.forwardRef(
(
updateFunction: (
previousValue: string[] | Record[]
- ) => string[] | Record[]
+ ) => string[] | Record[],
+ selectionConfirmed
) => {
const newValue = updateFunction(
actualValue as string[] | Record[]
);
// only call onChange if an option has been selected or deselected
if (onChange && newValue.length !== actualValue?.length) {
- onChange(createCustomEvent(newValue));
+ onChange(createCustomEvent(newValue, selectionConfirmed));
}
// no need to update selectedValue if the component is controlled: onChange should take care of updating the value
@@ -270,7 +273,7 @@ export const MultiSelect = React.forwardRef(
newValue.splice(index, 1);
return newValue;
- });
+ }, true);
},
[updateValue]
);
@@ -357,6 +360,11 @@ export const MultiSelect = React.forwardRef(
let pillProps: Omit = {};
+ if (!matchingOption) {
+ return null;
+ }
+
+ /* istanbul ignore else */
if (React.isValidElement(matchingOption)) {
pillProps = {
title: matchingOption.props.text,
@@ -365,12 +373,12 @@ export const MultiSelect = React.forwardRef(
};
}
- const title = pillProps.title || "";
+ const title = pillProps.title || /* istanbul ignore next */ "";
const key =
title +
((React.isValidElement(matchingOption) &&
(matchingOption.props as OptionProps | OptionRowProps).value) ||
- index);
+ /* istanbul ignore next */ index);
return (
@@ -534,6 +542,7 @@ export const MultiSelect = React.forwardRef(
value: newValue,
selectionType,
id: selectedOptionId,
+ selectionConfirmed,
} = optionData;
if (selectionType === "navigationKey") {
@@ -561,7 +570,7 @@ export const MultiSelect = React.forwardRef(
}
return [...previousValue, newValue];
- });
+ }, selectionConfirmed);
},
[textboxRef, actualValue, updateValue]
);
diff --git a/src/components/select/multi-select/multi-select.spec.tsx b/src/components/select/multi-select/multi-select.spec.tsx
index 7c1d61067b..2f658c9d59 100644
--- a/src/components/select/multi-select/multi-select.spec.tsx
+++ b/src/components/select/multi-select/multi-select.spec.tsx
@@ -77,6 +77,21 @@ describe("MultiSelect", () => {
).toBe(placeholder);
});
+ it("should not render an empty Pill when non-matching filter text is input and enter key pressed", () => {
+ const wrapper = renderSelect({});
+
+ act(() => {
+ wrapper.find(Textbox).prop("onChange")?.({
+ target: { value: "foo" },
+ } as React.ChangeEvent);
+ wrapper.find(Textbox).prop("onKeyDown")?.({
+ key: "Enter",
+ } as React.KeyboardEvent);
+ });
+
+ expect(wrapper.find(Pill).exists()).toBe(false);
+ });
+
describe("when an HTML element is clicked", () => {
let wrapper: ReactWrapper;
let domNode: HTMLElement;
@@ -484,6 +499,7 @@ describe("MultiSelect", () => {
value: "opt3",
text: "blue",
selectionType: "enter",
+ selectionConfirmed: true,
};
const changeEventObject = { target: { value: "b" } };
@@ -749,17 +765,20 @@ describe("MultiSelect", () => {
value: "opt1",
text: "red",
selectionType: "enter",
+ selectionConfirmed: true,
};
const mockNavigationKeyOptionObject = {
value: "opt1",
text: "red",
selectionType: "navigationKey",
+ selectionConfirmed: false,
};
const textboxProps = {
name: "testName",
id: "testId",
};
const expectedEventObject = {
+ selectionConfirmed: true,
target: {
...textboxProps,
value: ["opt1"],
@@ -958,6 +977,7 @@ describe("MultiSelect", () => {
describe("when the component is controlled", () => {
const expectedObject = {
+ selectionConfirmed: true,
target: {
id: "testSelect",
name: "testSelect",
@@ -969,6 +989,7 @@ describe("MultiSelect", () => {
value: "opt2",
text: "black",
selectionType: "click",
+ selectionConfirmed: true,
};
describe("and an option is selected", () => {
@@ -1069,12 +1090,14 @@ describe("MultiSelect", () => {
value: "opt1",
text: "red",
selectionType: "enter",
+ selectionConfirmed: true,
};
const textboxProps = {
name: "testName",
id: "testId",
};
const expectedEventObject = {
+ selectionConfirmed: true,
target: {
...textboxProps,
value: ["opt1"],
diff --git a/src/components/select/option/option.component.tsx b/src/components/select/option/option.component.tsx
index f17b7f2a7d..56ced45bb2 100644
--- a/src/components/select/option/option.component.tsx
+++ b/src/components/select/option/option.component.tsx
@@ -88,7 +88,7 @@ const Option = React.forwardRef(
role="option"
hidden={hidden}
style={style}
- {...rest}
+ {...{ ...rest, fill: undefined }}
>
{children || text}
diff --git a/src/components/select/select-list/select-list.component.tsx b/src/components/select/select-list/select-list.component.tsx
index 1d8c86b1af..0ea42b59bb 100644
--- a/src/components/select/select-list/select-list.component.tsx
+++ b/src/components/select/select-list/select-list.component.tsx
@@ -48,6 +48,7 @@ export interface SelectListProps {
value?: string | Record;
id?: string;
selectionType: string;
+ selectionConfirmed: boolean;
}) => void;
/** A callback for when the list should be closed */
onSelectListClose: () => void;
@@ -185,7 +186,11 @@ const SelectList = React.forwardRef(
const handleSelect = useCallback(
(optionData) => {
- onSelect({ ...optionData, selectionType: "click" });
+ onSelect({
+ ...optionData,
+ selectionType: "click",
+ selectionConfirmed: true,
+ });
},
[onSelect]
);
@@ -329,6 +334,7 @@ const SelectList = React.forwardRef(
id: childIds
? childIds[nextIndex]
: /* istanbul ignore next */ undefined,
+ selectionConfirmed: false,
});
},
[
@@ -345,7 +351,7 @@ const SelectList = React.forwardRef(
const handleActionButtonTab = useCallback(
(event, isActionButtonFocused) => {
if (isActionButtonFocused) {
- onSelect({ selectionType: "tab" });
+ onSelect({ selectionType: "tab", selectionConfirmed: false });
} else {
event.preventDefault();
listActionButtonRef.current?.focus();
@@ -393,6 +399,7 @@ const SelectList = React.forwardRef(
text,
value,
selectionType: "enterKey",
+ selectionConfirmed: true,
});
} else if (isNavigationKey(key)) {
focusOnAnchor();
diff --git a/src/components/select/select-list/select-list.spec.tsx b/src/components/select/select-list/select-list.spec.tsx
index 58409ca289..1591f93686 100644
--- a/src/components/select/select-list/select-list.spec.tsx
+++ b/src/components/select/select-list/select-list.spec.tsx
@@ -346,6 +346,7 @@ describe("SelectList", () => {
selectionType: "enterKey",
text: "blue",
value: "opt3",
+ selectionConfirmed: true,
});
});
});
@@ -363,6 +364,7 @@ describe("SelectList", () => {
selectionType: "navigationKey",
text: "red",
value: "opt1",
+ selectionConfirmed: false,
});
});
});
@@ -380,6 +382,7 @@ describe("SelectList", () => {
selectionType: "navigationKey",
text: "red",
value: "opt1",
+ selectionConfirmed: false,
});
});
@@ -410,6 +413,7 @@ describe("SelectList", () => {
selectionType: "navigationKey",
text: "blue",
value: "opt3",
+ selectionConfirmed: false,
});
});
});
@@ -427,6 +431,7 @@ describe("SelectList", () => {
selectionType: "navigationKey",
text: "blue",
value: "opt3",
+ selectionConfirmed: false,
});
});
});
@@ -443,6 +448,7 @@ describe("SelectList", () => {
selectionType: "navigationKey",
text: "red",
value: "opt1",
+ selectionConfirmed: false,
});
});
});
@@ -458,6 +464,7 @@ describe("SelectList", () => {
selectionType: "navigationKey",
text: "blue",
value: "opt3",
+ selectionConfirmed: false,
});
});
});
@@ -498,6 +505,7 @@ describe("SelectList", () => {
selectionType: "click",
text: "red",
value: "opt1",
+ selectionConfirmed: true,
});
});
});
@@ -771,7 +779,10 @@ describe("SelectList", () => {
const testContainer = document.createElement("div");
const onFocusFn = jest.fn();
const onSelectFn = jest.fn();
- const expectedSelectValue = { selectionType: "tab" };
+ const expectedSelectValue = {
+ selectionType: "tab",
+ selectionConfirmed: false,
+ };
document.body.appendChild(testContainer);
@@ -915,6 +926,7 @@ describe("SelectList", () => {
selectionType: "navigationKey",
text: "red",
value: "opt1",
+ selectionConfirmed: false,
});
});
});
diff --git a/src/components/select/select-textbox/select-textbox.component.tsx b/src/components/select/select-textbox/select-textbox.component.tsx
index 8f86fa400c..dd6cc5e2c9 100644
--- a/src/components/select/select-textbox/select-textbox.component.tsx
+++ b/src/components/select/select-textbox/select-textbox.component.tsx
@@ -6,6 +6,7 @@ import Textbox, { CommonTextboxProps } from "../../textbox";
import SelectText from "../__internal__/select-text";
import useLocale from "../../../hooks/__internal__/useLocale";
import { ValidationProps } from "../../../__internal__/validations";
+import { CustomSelectChangeEvent } from "../simple-select/simple-select.component";
const floatingMiddleware = [
offset(({ rects }) => ({
@@ -21,7 +22,7 @@ const floatingMiddleware = [
export interface FormInputPropTypes
extends ValidationProps,
- Omit {
+ Omit {
/** Breakpoint for adaptive label (inline labels change to top aligned). Enables the adaptive behaviour when set */
adaptiveLabelBreakpoint?: number;
/** Prop to specify the aria-label attribute of the component input */
@@ -49,7 +50,9 @@ export interface FormInputPropTypes
/** Specify a callback triggered on blur */
onBlur?: (ev: React.FocusEvent) => void;
/** Specify a callback triggered on change */
- onChange?: (ev: React.ChangeEvent) => void;
+ onChange?: (
+ ev: CustomSelectChangeEvent | React.ChangeEvent
+ ) => void;
/** Specify a callback triggered on click */
onClick?: (ev: React.MouseEvent) => void;
/** Specify a callback triggered on focus */
diff --git a/src/components/select/simple-select/index.ts b/src/components/select/simple-select/index.ts
index ab19d3d035..97a1553a58 100644
--- a/src/components/select/simple-select/index.ts
+++ b/src/components/select/simple-select/index.ts
@@ -1,2 +1,5 @@
export { default } from "./simple-select.component";
-export type { SimpleSelectProps } from "./simple-select.component";
+export type {
+ SimpleSelectProps,
+ CustomSelectChangeEvent,
+} from "./simple-select.component";
diff --git a/src/components/select/simple-select/simple-select-test.stories.tsx b/src/components/select/simple-select/simple-select-test.stories.tsx
index 5bc5935fcc..b8b278efd6 100644
--- a/src/components/select/simple-select/simple-select-test.stories.tsx
+++ b/src/components/select/simple-select/simple-select-test.stories.tsx
@@ -1,7 +1,10 @@
-import React, { useState } from "react";
+import React, { useState, useRef } from "react";
import Typography from "../../../components/typography";
import Content from "../../../components/content";
-import { Select as SimpleSelect } from "../../../../src/components/select";
+import {
+ CustomSelectChangeEvent,
+ Select as SimpleSelect,
+} from "../../../../src/components/select";
import OptionRow from "../option-row/option-row.component";
import OptionGroupHeader from "../option-group-header/option-group-header.component";
import Box from "../../box";
@@ -12,6 +15,7 @@ import { Select, Option, SimpleSelectProps } from "..";
export default {
component: Select,
title: "Select/Test",
+ excludeStories: ["SelectionConfirmed"],
parameters: {
info: { disable: true },
chromatic: {
@@ -169,7 +173,7 @@ export const DelayedReposition = () => {
DelayedReposition.storyName = "delayed reposition";
export const SimpleSelectComponent = (props: Partial) => {
- const [value, setValue] = React.useState("");
+ const [value, setValue] = useState("");
function onChangeHandler(event: React.ChangeEvent) {
setValue(event.target.value);
@@ -204,9 +208,9 @@ export const SimpleSelectComponent = (props: Partial) => {
export const SimpleSelectWithLazyLoadingComponent = (
props: Partial
) => {
- const preventLoading = React.useRef(false);
- const [value, setValue] = React.useState("black");
- const [isLoading, setIsLoading] = React.useState(true);
+ const preventLoading = useRef(false);
+ const [value, setValue] = useState("black");
+ const [isLoading, setIsLoading] = useState(true);
const asyncList = [
,
,
@@ -214,7 +218,7 @@ export const SimpleSelectWithLazyLoadingComponent = (
,
,
];
- const [optionList, setOptionList] = React.useState([
+ const [optionList, setOptionList] = useState([
,
]);
@@ -254,11 +258,11 @@ export const SimpleSelectWithLazyLoadingComponent = (
export const SimpleSelectWithInfiniteScrollComponent = (
props: Partial
) => {
- const preventLoading = React.useRef(false);
- const preventLazyLoading = React.useRef(false);
- const lazyLoadingCounter = React.useRef(0);
- const [value, setValue] = React.useState("");
- const [isLoading, setIsLoading] = React.useState(true);
+ const preventLoading = useRef(false);
+ const preventLazyLoading = useRef(false);
+ const lazyLoadingCounter = useRef(0);
+ const [value, setValue] = useState("");
+ const [isLoading, setIsLoading] = useState(true);
const asyncList = [
,
,
@@ -349,7 +353,7 @@ export const SimpleSelectObjectAsValueComponent = (
value: 5,
text: "Green",
});
- const optionList = React.useRef([
+ const optionList = useRef([