Skip to content

Commit

Permalink
feat: add iconLeft prop to AsyncSelect
Browse files Browse the repository at this point in the history
  • Loading branch information
haideralsh committed Dec 16, 2024
1 parent 31ccccb commit c9aea1b
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 28 deletions.
20 changes: 13 additions & 7 deletions src/AsyncSelect/AsyncSelect.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ export const Default = () => (
/>
);

Default.story = {
name: "default",
};

export const WithDefaultOptions = () => (
<AsyncSelect
placeholder="Enter a province"
Expand Down Expand Up @@ -147,6 +143,16 @@ export const Controlled = () => {
);
};

Controlled.story = {
name: "controlled",
};
export const WithIcon = () => (
<AsyncSelect
iconLeft="search"
placeholder="Search for a province"
onChange={action("selection changed")}
onBlur={action("blurred")}
className="Select"
classNamePrefix="SelectTest"
labelText="Province"
onInputChange={action("typed input value changed")}
loadOptions={loadMatchingProvinces}
/>
);
10 changes: 9 additions & 1 deletion src/AsyncSelect/AsyncSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { forwardRef, ReactNode, MutableRefObject } from "react";
import { IconName } from "@nulogy/icons";
import Select from "react-select/base";
import AsyncReactSelect from "react-select/async";
import { AsyncProps } from "react-select/async";
Expand Down Expand Up @@ -36,6 +37,7 @@ type AsyncCustomProps<Option, IsMulti extends boolean, Group extends GroupBase<O
multiselect?: AsyncProps<Option, IsMulti, Group>["isMulti"];
maxHeight?: string;
defaultValue?: AsyncProps<Option, IsMulti, Group>["defaultInputValue"];
iconLeft?: IconName | "loading";
};

export type AsyncSelectProps<Option, IsMulti extends boolean, Group extends GroupBase<Option>> = Omit<
Expand Down Expand Up @@ -80,6 +82,7 @@ const AsyncSelect = forwardRef(
loadOptions,
isClearable,
variant,
iconLeft,
...props
}: AsyncSelectProps<Option, IsMulti, Group>,
ref:
Expand All @@ -106,6 +109,7 @@ const AsyncSelect = forwardRef(
defaultInputValue={defaultValue}
placeholder={placeholder || t("start typing")}
styles={customStyles<Option, IsMulti, Group>({
hasIcon: Boolean(iconLeft),
theme,
error,
maxHeight,
Expand Down Expand Up @@ -135,7 +139,11 @@ const AsyncSelect = forwardRef(
{props.children}
</SelectOption>
),
Control: SelectControl,
Control: (props) => (
<SelectControl iconLeft={iconLeft} {...props}>
{props.children}
</SelectControl>
),
MultiValue: SelectMultiValue,
ClearIndicator: SelectClearIndicator,
DropdownIndicator: SelectDropdownIndicator,
Expand Down
18 changes: 12 additions & 6 deletions src/AsyncSelect/AsyncSelectComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,26 @@ import type { OptionProps } from "react-windowed-select";
import { useComponentVariant } from "../NDSProvider/ComponentVariantContext";
import type { ComponentVariant } from "../NDSProvider/ComponentVariantContext";
import { StyledOption } from "../Select/SelectOption";
import { IconName } from "@nulogy/icons";
import { InputIcon } from "../Icon/Icon";

export const SelectControl = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
props: ControlProps<Option, IsMulti, Group>
) => {
// eslint-disable-next-line react/prop-types
const { isFocused } = props;
export const SelectControl = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>({
iconLeft,
isFocused,
children,
...props
}: ControlProps<Option, IsMulti, Group> & {
iconLeft?: IconName | "loading";
}) => {
return (
<div data-testid="select-control">
<selectComponents.Control
className={isFocused ? "nds-select--is-focused" : null}
isFocused={isFocused}
{...props}
>
{props.children}
{iconLeft && <InputIcon left="x1" icon={iconLeft} size="x3" />}
{children}
</selectComponents.Control>
</div>
);
Expand Down
13 changes: 12 additions & 1 deletion src/Icon/Icon.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import styled, { useTheme } from "styled-components";
import { space, SpaceProps } from "styled-system";
import { position, PositionProps, space, SpaceProps } from "styled-system";
import icons from "@nulogy/icons";
import { IconName } from "@nulogy/icons";
import LoadingIcon from "./LoadingIcon";
Expand Down Expand Up @@ -101,4 +101,15 @@ export function InlineIcon(props: IconProps) {
);
}

export const InputIcon = styled(Icon)<PositionProps>(
({ theme }) => ({
position: "absolute",
color: theme.colors.midGrey,
bottom: "50%",
transform: "translateY(50%)",
pointerEvents: "none",
}),
position
);

export default Icon;
16 changes: 3 additions & 13 deletions src/Input/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { DefaultNDSThemeType } from "../theme";
import { ComponentVariant, useComponentVariant } from "../NDSProvider/ComponentVariantContext";
import Prefix from "./Prefix";
import Suffix from "./Suffix";
import { InputIcon } from "../Icon/Icon";

type NativeInputProps = Omit<React.ComponentPropsWithRef<"input">, "size" | "height" | "width">;

Expand Down Expand Up @@ -67,7 +68,7 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(
<Flex alignItems="flex-start">
<Prefix prefix={prefix} prefixWidth={prefixWidth} textAlign={prefixAlignment} />
<InputWrapper maxWidth={inputWidth}>
{iconLeft && <StyledInputIcon left="x1" icon={iconLeft} size={iconLeftSize} variant={variant} />}
{iconLeft && <InputIcon left="x1" icon={iconLeft} size={iconLeftSize} />}
<StyledInput
paddingLeft={iconLeft ? `calc(${theme.space[iconLeftSize]} + ${theme.space.x1_5})` : theme.space.x1}
paddingRight={iconRight ? `calc(${theme.space[iconRightSize]} + ${theme.space.x1_5})` : theme.space.x1}
Expand All @@ -81,7 +82,7 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(
inputWidth={inputWidth}
{...props}
/>
{iconRight && <StyledInputIcon right="x1" icon={iconRight} size={iconRightSize} variant={variant} />}
{iconRight && <InputIcon right="x1" icon={iconRight} size={iconRightSize} />}
</InputWrapper>
<Suffix suffix={suffix} suffixWidth={suffixWidth} textAlign={suffixAlignment} />
</Flex>
Expand Down Expand Up @@ -143,17 +144,6 @@ const StyledInput = styled.input<StyledInputProps>(
space
);

const StyledInputIcon = styled(Icon)<{ variant: ComponentVariant } & PositionProps>(
({ theme }) => ({
position: "absolute",
color: theme.colors.midGrey,
bottom: "50%",
transform: "translateY(50%)",
pointerEvents: "none",
}),
position
);

const cssForState = ({
disabled,
error,
Expand Down
4 changes: 4 additions & 0 deletions src/Select/customReactSelectStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const customStyles: <Option, IsMulti extends boolean, Group extends GroupBase<Op
error,
maxHeight,
windowed,
hasIcon,
variant,
hasDefaultOptions = true,
}) => {
Expand Down Expand Up @@ -169,6 +170,9 @@ const customStyles: <Option, IsMulti extends boolean, Group extends GroupBase<Op
theme,
}),
},
...(hasIcon && {
paddingLeft: `calc(${theme.space.x3} + ${theme.space.x1_5})`,
}),
}),
dropdownIndicator: (provided) => ({
...provided,
Expand Down

0 comments on commit c9aea1b

Please sign in to comment.