diff --git a/.changeset/dry-years-sleep.md b/.changeset/dry-years-sleep.md
new file mode 100644
index 00000000..3aaa7936
--- /dev/null
+++ b/.changeset/dry-years-sleep.md
@@ -0,0 +1,5 @@
+---
+"@heathmont/moon-core-tw": minor
+---
+
+fix: open options list on input focus [MDS-1378]
diff --git a/docs/app/client/icons/icons/IconWrapper.tsx b/docs/app/client/icons/icons/IconWrapper.tsx
index 001d5e77..69bf05ac 100644
--- a/docs/app/client/icons/icons/IconWrapper.tsx
+++ b/docs/app/client/icons/icons/IconWrapper.tsx
@@ -62,9 +62,7 @@ const IconWrapper = ({ children, name }: IconProps) => {
>
- Icon {renderIcon(
- name,
- )} copied for import
+ Icon {renderIcon(name)} copied for import
>
diff --git a/docs/app/client/icons/search/IconSearch.tsx b/docs/app/client/icons/search/IconSearch.tsx
index f34f6ec5..aeb60713 100644
--- a/docs/app/client/icons/search/IconSearch.tsx
+++ b/docs/app/client/icons/search/IconSearch.tsx
@@ -113,9 +113,7 @@ const IconSearch = () => {
>
- Icon {renderIcon(
- lastClickedIcon,
- )} copied for import
+ Icon {renderIcon(lastClickedIcon)} copied for import
diff --git a/packages/core/src/combobox/Combobox.tsx b/packages/core/src/combobox/Combobox.tsx
index 5d8e1062..a0a0df8a 100644
--- a/packages/core/src/combobox/Combobox.tsx
+++ b/packages/core/src/combobox/Combobox.tsx
@@ -1,4 +1,4 @@
-import React, { forwardRef, useEffect, useState } from "react";
+import React, { forwardRef, useEffect } from "react";
import {
Combobox as HeadlessCombobox,
Transition as HeadlessTransition,
@@ -52,19 +52,50 @@ const ComboboxRoot = ({
placement: position,
});
+ const comboboxButtonRef = React.useRef(null);
+ const blurredRef = React.useRef(false);
+ const prevSelected = React.useRef({});
+
+ const handleOnFocus: React.FocusEventHandler = (event) => {
+ setIsInputFocused(true);
+
+ if (event.relatedTarget?.id?.includes("headlessui-combobox-button")) {
+ return;
+ }
+
+ comboboxButtonRef?.current?.click();
+ };
+
+ const handleOnKeyDown: React.KeyboardEventHandler = (
+ event,
+ ) => {
+ if (event.key === "Tab") {
+ blurredRef.current = true;
+ prevSelected.current = value;
+ console.log("in here oe key down tab");
+ }
+ };
+
+ const handleOnBlur: React.FocusEventHandler = () => {
+ setIsInputFocused(false);
+ if (blurredRef.current) {
+ onChange(prevSelected.current);
+ }
+ };
+
const states = {
- value: value,
- displayValue: displayValue,
- isError: isError,
- size: size,
- disabled: disabled,
+ value,
+ displayValue,
+ isError,
+ size,
+ disabled,
input: {
isFocused: isInputFocused,
setIsFocused: setIsInputFocused,
},
- multiple: multiple,
- onClear: onClear,
- onQueryChange: onQueryChange,
+ multiple,
+ onClear,
+ onQueryChange,
popper: {
forceUpdate,
styles,
@@ -72,6 +103,10 @@ const ComboboxRoot = ({
setAnchor: setAnchorEl,
setPopper: setPopperEl,
},
+ comboboxButtonRef,
+ handleOnFocus,
+ handleOnBlur,
+ handleOnKeyDown,
};
const childArray =
@@ -152,8 +187,17 @@ const Input = ({
label,
...rest
}: InputProps) => {
- const { size, popper, disabled, isError, input, onQueryChange } =
- useComboboxContext("Combobox.Input");
+ const {
+ size,
+ popper,
+ disabled,
+ isError,
+ onQueryChange,
+ handleOnFocus,
+ handleOnKeyDown,
+ handleOnBlur,
+ } = useComboboxContext("Combobox.Input");
+
return (
{
@@ -171,8 +215,9 @@ const Input = ({
)}
disabled={disabled}
error={isError}
- onFocus={() => input?.setIsFocused(true)}
- onBlur={() => input?.setIsFocused(false)}
+ onFocus={handleOnFocus}
+ onKeyDown={handleOnKeyDown}
+ onBlur={handleOnBlur}
aria-label={rest["aria-label"]}
{...rest}
ref={popper?.setAnchor}
@@ -188,8 +233,16 @@ const InsetInput = ({
label,
...rest
}: InputProps) => {
- const { size, popper, disabled, isError, input, onQueryChange } =
- useComboboxContext("Combobox.InsetInput");
+ const {
+ size,
+ popper,
+ disabled,
+ isError,
+ onQueryChange,
+ handleOnFocus,
+ handleOnKeyDown,
+ handleOnBlur,
+ } = useComboboxContext("Combobox.InsetInput");
return (
input?.setIsFocused(true)}
- onBlur={() => input?.setIsFocused(false)}
+ onFocus={handleOnFocus}
+ onKeyDown={handleOnKeyDown}
+ onBlur={handleOnBlur}
aria-label={rest["aria-label"]}
{...rest}
ref={popper?.setAnchor}
@@ -235,8 +289,17 @@ const VisualSelectInput = ({
label,
...rest
}: InputProps) => {
- const { value, size, popper, disabled, isError, onQueryChange } =
- useComboboxContext("Combobox.VisualSelectInput");
+ const {
+ value,
+ size,
+ popper,
+ disabled,
+ isError,
+ onQueryChange,
+ handleOnFocus,
+ handleOnKeyDown,
+ handleOnBlur,
+ } = useComboboxContext("Combobox.VisualSelectInput");
const selected = value as [];
return (
@@ -276,6 +339,9 @@ const VisualSelectInput = ({
aria-label={rest["aria-label"]}
{...rest}
ref={popper?.setAnchor}
+ onFocus={handleOnFocus}
+ onKeyDown={handleOnKeyDown}
+ onBlur={handleOnBlur}
/>
);
@@ -289,8 +355,10 @@ const Button = ({
["aria-label"]: ariaLabel,
...rest
}: WithChildren) => {
- const { size, disabled } = useComboboxContext("Combobox.Button");
+ const { size, disabled, comboboxButtonRef, input } =
+ useComboboxContext("Combobox.Button");
const ariaLabelValue = ariaLabel ? ariaLabel : open ? "Close" : "Open";
+
return (
@@ -313,14 +382,16 @@ const Options = ({
children,
menuWidth,
className,
+ open,
...rest
}: WithChildren) => {
const { popper } = useComboboxContext("Combobox.Options");
- return (
+ const OptionsComponent = (
);
+
+ if (open === undefined) {
+ return OptionsComponent;
+ }
+
+ return open && OptionsComponent;
};
const Option = ({ children, value }: OptionProps) => {
diff --git a/packages/core/src/combobox/private/types/ComboboxState.ts b/packages/core/src/combobox/private/types/ComboboxState.ts
index 20c64283..303bccba 100644
--- a/packages/core/src/combobox/private/types/ComboboxState.ts
+++ b/packages/core/src/combobox/private/types/ComboboxState.ts
@@ -23,6 +23,10 @@ type ComboboxState = {
>;
};
size?: Size;
+ comboboxButtonRef?: React.MutableRefObject;
+ handleOnFocus?: React.FocusEventHandler;
+ handleOnBlur?: React.FocusEventHandler;
+ handleOnKeyDown?: React.KeyboardEventHandler;
};
export default ComboboxState;
diff --git a/packages/core/src/combobox/private/types/OptionsProps.ts b/packages/core/src/combobox/private/types/OptionsProps.ts
index 746cc694..5e941050 100644
--- a/packages/core/src/combobox/private/types/OptionsProps.ts
+++ b/packages/core/src/combobox/private/types/OptionsProps.ts
@@ -1,6 +1,7 @@
type OptionsProps = {
menuWidth?: string;
className?: string;
+ open?: boolean;
};
export default OptionsProps;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0caf4239..215f95e4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1952,7 +1952,7 @@ packages:
resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==}
engines: {node: '>=10'}
dependencies:
- tslib: 2.6.2
+ tslib: 2.7.0
dev: false
/aria-query@5.3.0:
@@ -5370,7 +5370,7 @@ packages:
'@types/react': 18.2.37
react: 18.2.0
react-style-singleton: 2.2.1(@types/react@18.2.37)(react@18.2.0)
- tslib: 2.6.2
+ tslib: 2.7.0
dev: false
/react-remove-scroll@2.5.4(@types/react@18.2.37)(react@18.2.0):
@@ -5387,7 +5387,7 @@ packages:
react: 18.2.0
react-remove-scroll-bar: 2.3.4(@types/react@18.2.37)(react@18.2.0)
react-style-singleton: 2.2.1(@types/react@18.2.37)(react@18.2.0)
- tslib: 2.6.2
+ tslib: 2.7.0
use-callback-ref: 1.3.0(@types/react@18.2.37)(react@18.2.0)
use-sidecar: 1.1.2(@types/react@18.2.37)(react@18.2.0)
dev: false
@@ -5406,7 +5406,7 @@ packages:
get-nonce: 1.0.1
invariant: 2.2.4
react: 18.2.0
- tslib: 2.6.2
+ tslib: 2.7.0
dev: false
/react-syntax-highlighter@15.5.0(react@18.2.0):
@@ -6198,7 +6198,6 @@ packages:
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
requiresBuild: true
dev: false
- optional: true
/tty-table@4.2.3:
resolution: {integrity: sha512-Fs15mu0vGzCrj8fmJNP7Ynxt5J7praPXqFN0leZeZBXJwkMxv9cb2D454k1ltrtUSJbZ4yH4e0CynsHLxmUfFA==}
@@ -6400,7 +6399,7 @@ packages:
dependencies:
'@types/react': 18.2.37
react: 18.2.0
- tslib: 2.6.2
+ tslib: 2.7.0
dev: false
/use-sidecar@1.1.2(@types/react@18.2.37)(react@18.2.0):
@@ -6416,7 +6415,7 @@ packages:
'@types/react': 18.2.37
detect-node-es: 1.1.0
react: 18.2.0
- tslib: 2.6.2
+ tslib: 2.7.0
dev: false
/util-deprecate@1.0.2: