diff --git a/src/domains/profile/components/SelectRecipient/SelectRecipient.stories.tsx b/src/domains/profile/components/SelectRecipient/SelectRecipient.stories.tsx
new file mode 100644
index 0000000000..637cb14ceb
--- /dev/null
+++ b/src/domains/profile/components/SelectRecipient/SelectRecipient.stories.tsx
@@ -0,0 +1,30 @@
+import { contacts } from "domains/contact/data";
+import React from "react";
+
+import { SelectRecipient } from "./SelectRecipient";
+
+export default { title: "Domains / Profile / Components / Select Recipient" };
+
+export const Default = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
Selected address (disabled)
+
+
+
+ );
+};
diff --git a/src/domains/profile/components/SelectRecipient/SelectRecipient.test.tsx b/src/domains/profile/components/SelectRecipient/SelectRecipient.test.tsx
new file mode 100644
index 0000000000..ccf500da42
--- /dev/null
+++ b/src/domains/profile/components/SelectRecipient/SelectRecipient.test.tsx
@@ -0,0 +1,134 @@
+/* eslint-disable @typescript-eslint/require-await */
+import { contacts } from "domains/contact/data";
+import React from "react";
+import { act, fireEvent, render, waitFor } from "testing-library";
+
+import { SelectRecipient } from "./SelectRecipient";
+
+describe("SelectRecipient", () => {
+ it("should render empty", () => {
+ const { container } = render();
+ expect(container).toMatchSnapshot();
+ });
+
+ it("should render disabled", () => {
+ const { container } = render();
+ expect(container).toMatchSnapshot();
+ });
+
+ it("should render invalid", () => {
+ const { container } = render();
+ expect(container).toMatchSnapshot();
+ });
+
+ it("should render with preselected address", () => {
+ const { container } = render(
+ ,
+ );
+ expect(container).toMatchSnapshot();
+ });
+
+ it("should open and close contacts modal", () => {
+ const { getByTestId } = render(
+ ,
+ );
+
+ expect(() => getByTestId("modal__inner")).toThrow(/Unable to find an element by/);
+
+ act(() => {
+ fireEvent.click(getByTestId("SelectRecipient__select-contact"));
+ });
+
+ expect(getByTestId("modal__inner")).toBeTruthy();
+
+ act(() => {
+ fireEvent.click(getByTestId("modal__close-btn"));
+ });
+
+ waitFor(() => expect(getByTestId("modal__inner")).toBeFalsy());
+ });
+
+ it("should select address from contacts modal", () => {
+ const { getByTestId, getAllByTestId } = render(
+ ,
+ );
+
+ expect(() => getByTestId("modal__inner")).toThrow(/Unable to find an element by/);
+
+ act(() => {
+ fireEvent.click(getByTestId("SelectRecipient__select-contact"));
+ });
+
+ expect(getByTestId("modal__inner")).toBeTruthy();
+
+ const firstAddress = getAllByTestId("ContactListItem__one-option-button-0")[0];
+
+ act(() => {
+ fireEvent.click(firstAddress);
+ });
+
+ waitFor(() => {
+ expect(getByTestId("modal__inner").toThrow(/Unable to find an element by/));
+
+ const selectedAddressValue = contacts[0]?.addresses()[0]?.address;
+ expect(getByTestId("SelectRecipient__input")).toHaveValue(selectedAddressValue);
+ });
+ });
+
+ it("should not open contacts modal if disabled", () => {
+ const { getByTestId } = render(
+ ,
+ );
+
+ expect(() => getByTestId("modal__inner")).toThrow(/Unable to find an element by/);
+
+ act(() => {
+ fireEvent.click(getByTestId("SelectRecipient__select-contact"));
+ });
+
+ expect(() => getByTestId("modal__inner")).toThrow(/Unable to find an element by/);
+ });
+
+ it("should call onChange prop when entered address in input", async () => {
+ const fn = jest.fn();
+ const { getByTestId } = render();
+ const address = "bP6T9GQ3kqP6T9GQ3kqP6T9GQ3kqTTTP6T9GQ3kqT";
+ const recipientInputField = getByTestId("SelectRecipient__input");
+
+ await act(async () => {
+ fireEvent.change(recipientInputField, { target: { value: address } });
+ });
+
+ expect(fn).toBeCalledWith(address);
+ });
+
+ it("should call onChange prop if provided", () => {
+ const fn = jest.fn();
+ const { getByTestId, getAllByTestId } = render(
+ ,
+ );
+
+ expect(() => getByTestId("modal__inner")).toThrow(/Unable to find an element by/);
+
+ act(() => {
+ fireEvent.click(getByTestId("SelectRecipient__select-contact"));
+ });
+
+ expect(getByTestId("modal__inner")).toBeTruthy();
+
+ const firstAddress = getAllByTestId("ContactListItem__one-option-button-0")[0];
+
+ act(() => {
+ fireEvent.click(firstAddress);
+ });
+
+ waitFor(() => {
+ expect(getByTestId("modal__inner").toThrow(/Unable to find an element by/));
+
+ const selectedAddressValue = contacts[0]?.addresses()[0]?.address;
+ expect(getByTestId("SelectRecipient__input")).toHaveValue(selectedAddressValue);
+
+ expect(fn).toBeCalledWith(selectedAddressValue);
+ });
+ });
+});
diff --git a/src/domains/profile/components/SelectRecipient/SelectRecipient.tsx b/src/domains/profile/components/SelectRecipient/SelectRecipient.tsx
new file mode 100644
index 0000000000..1a19b1a462
--- /dev/null
+++ b/src/domains/profile/components/SelectRecipient/SelectRecipient.tsx
@@ -0,0 +1,108 @@
+import { Avatar } from "app/components/Avatar";
+import { Circle } from "app/components/Circle";
+import { useFormField } from "app/components/Form/useFormField";
+import { Icon } from "app/components/Icon";
+import { Input } from "app/components/Input";
+import { SearchContact } from "domains/contact/components/SearchContact";
+import React, { useEffect, useState } from "react";
+
+type SelectRecipientProps = {
+ address?: string;
+ contacts: any[];
+ disabled?: boolean;
+ isInvalid?: boolean;
+ contactSearchTitle?: string;
+ contactSearchDescription?: string;
+ selectActionLabel?: string;
+ onChange?: (address: string) => void;
+} & React.InputHTMLAttributes;
+
+const ProfileAvatar = ({ address }: any) => {
+ if (!address) return ;
+ return ;
+};
+
+export const SelectRecipient = React.forwardRef(
+ (
+ {
+ contactSearchTitle,
+ contactSearchDescription,
+ selectActionLabel,
+ address,
+ contacts,
+ disabled,
+ isInvalid,
+ onChange,
+ }: SelectRecipientProps,
+ ref,
+ ) => {
+ const [isContactSearchOpen, setIsContactSearchOpen] = useState(false);
+ const [selectedAddress, setSelectedAddress] = useState(address);
+ useEffect(() => setSelectedAddress(address), [address]);
+
+ const fieldContext = useFormField();
+ const isInvalidField = fieldContext?.isInvalid || isInvalid;
+
+ const onSelectProfile = (address: any) => {
+ setSelectedAddress(address);
+ setIsContactSearchOpen(false);
+ onChange?.(address);
+ };
+
+ const openContacts = () => {
+ if (disabled) return;
+ setIsContactSearchOpen(true);
+ };
+
+ const onInputChange = (value: string) => {
+ setSelectedAddress(value);
+ onChange?.(value);
+ };
+
+ return (
+
+
+
+
onInputChange?.(ev.target.value)}
+ disabled={disabled}
+ isInvalid={isInvalidField}
+ />
+
+
+
+
+
+
+
onSelectProfile(address)}
+ onClose={() => setIsContactSearchOpen(false)}
+ />
+
+ );
+ },
+);
+
+SelectRecipient.defaultProps = {
+ contactSearchTitle: "Recipient search",
+ contactSearchDescription: "Find and select the recipient from your contacts",
+ selectActionLabel: "Select",
+};
+
+SelectRecipient.displayName = "SelectRecipient";
diff --git a/src/domains/profile/components/SelectRecipient/__snapshots__/SelectRecipient.test.tsx.snap b/src/domains/profile/components/SelectRecipient/__snapshots__/SelectRecipient.test.tsx.snap
new file mode 100644
index 0000000000..b5449c2505
--- /dev/null
+++ b/src/domains/profile/components/SelectRecipient/__snapshots__/SelectRecipient.test.tsx.snap
@@ -0,0 +1,170 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SelectRecipient should render disabled 1`] = `
+
+`;
+
+exports[`SelectRecipient should render empty 1`] = `
+
+`;
+
+exports[`SelectRecipient should render invalid 1`] = `
+
+`;
+
+exports[`SelectRecipient should render with preselected address 1`] = `
+
+
+
+
+
+
+
"
+ title="bP6T9GQ3kqP6T9GQ3kqP6T9GQ3kqTTTP6T9GQ3kqT"
+ />
+
+
+
+
+
+
+
+
+`;
diff --git a/src/domains/profile/components/SelectRecipient/index.ts b/src/domains/profile/components/SelectRecipient/index.ts
new file mode 100644
index 0000000000..13aeb6380f
--- /dev/null
+++ b/src/domains/profile/components/SelectRecipient/index.ts
@@ -0,0 +1 @@
+export * from "./SelectRecipient";