Skip to content

Commit

Permalink
feat(Search): new Search component (#2064)
Browse files Browse the repository at this point in the history
  • Loading branch information
YossiSaadi authored Apr 17, 2024
1 parent c694d7d commit 489a374
Show file tree
Hide file tree
Showing 34 changed files with 868 additions and 238 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createComponentTemplate, StoryDescription } from "vibe-storybook-compon
import DialogContentContainer from "../../DialogContentContainer/DialogContentContainer";
import { Info, Invite, ThumbsUp } from "../../Icon/Icons";
import Icon from "../../Icon/Icon";
import Search from "../../Search/Search";
import LegacySearch from "../../LegacySearch/LegacySearch";
import Avatar from "../../Avatar/Avatar";
import person from "./assets/person.png";
import Flex from "../../Flex/Flex";
Expand Down Expand Up @@ -159,7 +159,7 @@ export const AttentionBoxInsideADialogCombobox = {

return (
<DialogContentContainer className="monday-storybook-attention-box_search-bar">
<Search placeholder="Search by name, role, team, or email" />
<LegacySearch placeholder="Search by name, role, team, or email" />
<div className="monday-storybook-attention-box_lable">Suggested people</div>
<div className="monday-storybook-attention-box_search">
<div className="monday-storybook-attention-box_inline-container">
Expand Down
30 changes: 18 additions & 12 deletions packages/core/src/components/BaseInput/BaseInput.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,49 @@
import React, { forwardRef } from "react";
import cx from "classnames";
import styles from "./BaseInput.module.scss";
import { BaseInputComponent } from "./BaseInput.types";
import { BaseInputProps } from "./BaseInput.types";
import { getStyle } from "../../helpers/typesciptCssModulesHelper";

const BaseInput: BaseInputComponent = forwardRef(
const BaseInput = forwardRef(
(
{
size = "medium",
leftRender,
rightRender,
renderLeft,
renderRight,
success,
error,
wrapperRole,
inputRole,
className,
wrapperClassName,
inputClassName,
...props
},
ref
}: BaseInputProps,
ref: React.ForwardedRef<HTMLInputElement>
) => {
const wrapperClassNames = cx(
styles.wrapper,
{
[styles.rightThinnerPadding]: !rightRender,
[styles.rightThinnerPadding]: !renderRight,
[styles.error]: error,
[styles.success]: success,
[styles.readOnly]: props.readOnly,
[styles.disabled]: props.disabled
},
getStyle(styles, size),
wrapperClassName
className
);

return (
<div className={wrapperClassNames} role={wrapperRole}>
{leftRender}
<input {...props} ref={ref} className={cx(styles.input, className)} aria-invalid={error} role={inputRole} />
{rightRender}
{renderLeft}
<input
{...props}
ref={ref}
className={cx(styles.input, inputClassName)}
aria-invalid={error}
role={inputRole}
/>
{renderRight}
</div>
);
}
Expand Down
40 changes: 34 additions & 6 deletions packages/core/src/components/BaseInput/BaseInput.types.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,49 @@
import { AriaRole, InputHTMLAttributes, ReactNode } from "react";
import { VibeComponentProps } from "../../types";
import { BASE_SIZES } from "../../constants";
import VibeComponent from "../../types/VibeComponent";

export type InputSize = (typeof BASE_SIZES)[keyof typeof BASE_SIZES];
type BaseInputNativeInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "role">;
type Renderer = ReactNode | ReactNode[];

export interface BaseInputProps extends BaseInputNativeInputProps, VibeComponentProps {
/**
* Size of the input element. Will influence also padding and font size.
*/
size?: InputSize;
leftRender?: Renderer;
rightRender?: Renderer;
/**
* A render prop function for adding a component or element to the left side of the input.
* This could be an icon, text, or any custom element that fits within the input's design.
*/
renderLeft?: Renderer;
/**
* Similar to renderLeft, but for adding an element to the right side of the input.
* Useful for clear buttons, password visibility toggles, or custom validation icons.
*/
renderRight?: Renderer;
/**
* When true, indicates that the input has successfully passed validation or meets some criteria.
* This control the visual styling of the input to convey success to the user.
*/
success?: boolean;
/**
* When true, indicates that there is an error with the input's current value.
* This control the visual styling of the input to convey error to the user.
*/
error?: boolean;
/**
* ARIA role for the input wrapper. This can be used to improve accessibility by
* giving screen readers more context about the input's purpose.
*/
wrapperRole?: AriaRole;
/**
* ARIA role for the input. Setting this helps in making the input more
* accessible by providing additional semantic information to assistive technologies.
*/
inputRole?: AriaRole;
wrapperClassName?: string;
/**
* Additional CSS class names to be applied to the input element. This allows for custom
* styling on top of the default styles provided by the component.
*/
inputClassName?: string;
}

export type BaseInputComponent = VibeComponent<BaseInputProps, HTMLInputElement>;
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ describe("BaseInput", () => {
});

it("should show left and right elements when provided", () => {
const leftRender = <div>Left</div>;
const rightRender = <div>Right</div>;
const { getByText } = renderBaseInput({ leftRender, rightRender });
const renderLeft = <div>Left</div>;
const renderRight = <div>Right</div>;
const { getByText } = renderBaseInput({ renderLeft, renderRight });

expect(getByText("Left")).toBeInTheDocument();
expect(getByText("Right")).toBeInTheDocument();
Expand All @@ -42,7 +42,7 @@ describe("BaseInput", () => {
});

it("should apply the className for input and wrapperClassName for wrapper", () => {
const { getByLabelText } = renderBaseInput({ className: "inputClass", wrapperClassName: "customWrapper" });
const { getByLabelText } = renderBaseInput({ className: "customWrapper", inputClassName: "inputClass" });
expect(getByLabelText("base-input")).toHaveClass("inputClass");
expect(getByLabelText("base-input").parentNode).toHaveClass("customWrapper");
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Chips from "../Chips";
import Text from "../../Text/Text";
import { createStoryMetaSettingsDecorator } from "../../../storybook";
import { createComponentTemplate } from "vibe-storybook-components";
import Search from "../../Search/Search";
import LegacySearch from "../../LegacySearch/LegacySearch";
import Avatar from "../../Avatar/Avatar";
import DialogContentContainer from "../../DialogContentContainer/DialogContentContainer";
import { Email } from "../../Icon/Icons";
Expand Down Expand Up @@ -187,7 +187,7 @@ export const ColorfulChipsForDifferentContent = {
<div className={styles.item}>
<Chips label="January" color={Chips.colors.POSITIVE} />
</div>
<Search />
<LegacySearch />
<div className={styles.item}>
<Chips label="August" readOnly color={Chips.colors.LIPSTICK} />
</div>
Expand All @@ -206,7 +206,7 @@ export const ColorfulChipsForDifferentContent = {
export const ChipsInAPersonPickerComboBox = {
render: () => (
<DialogContentContainer className={styles.searchBar}>
<Search placeholder="Search names, positions, or a team" />
<LegacySearch placeholder="Search names, positions, or a team" />
<Flex align={Flex.align.CENTER} justify={Flex.justify.CENTER}>
<Chips label="Esther Schanler" leftAvatar={person1} />
<Chips label="Rotem Dekel" leftAvatar={rotem} />
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/components/Combobox/Combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isFunction, noop as NOOP } from "lodash-es";
import { getStyle } from "../../helpers/typesciptCssModulesHelper";
import { ComponentDefaultTestId, getTestId } from "../../tests/test-ids-utils";
import useMergeRef from "../../hooks/useMergeRef";
import Search from "../Search/Search";
import LegacySearch from "../LegacySearch/LegacySearch";
import { BASE_SIZES } from "../../constants";
import Button from "../Button/Button";
import Text from "../Text/Text";
Expand Down Expand Up @@ -283,7 +283,7 @@ const Combobox: React.FC<ComboboxProps> & {
ellipsis={false}
>
<div className={styles.comboboxList} style={{ maxHeight: optionsListHeight }} role="listbox">
<Search
<LegacySearch
ref={inputRef}
value={filterValue}
wrapperClassName={cx(styles.comboboxSearchWrapper, searchWrapperClassName)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ONE_LINE_ELLIPSIS_TEST_ID, OVERFLOW_TITLE_CONTAINER_ID } from "../__tes
import { headingOverflowSuite } from "../__tests__/Heading.interactions";
import Divider from "../../Divider/Divider";
import Text from "../../Text/Text";
import Search from "../../Search/Search";
import LegacySearch from "../../LegacySearch/LegacySearch";
import Checkbox from "../../Checkbox/Checkbox";
import Button from "../../Button/Button";
import { Custom } from "../../Icon/Icons";
Expand Down Expand Up @@ -156,7 +156,7 @@ export const BuiltInPageHeaderNotEditable = {
</Heading>
<Divider className={styles.pageDivider} />
<Flex align={Flex.align.CENTER} gap={Flex.gaps.SMALL} aria-labelledby="my-work-id">
<Search wrapperClassName={styles.pageHeaderSearch} placeholder="Search" />
<LegacySearch wrapperClassName={styles.pageHeaderSearch} placeholder="Search" />
<Checkbox label="Hide done items" checked />
<Button leftIcon={Custom} kind={Button.kinds.TERTIARY}>
Customize
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/components/Icon/__stories__/IconsList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from "react";
import { iconsMetaData } from "monday-ui-style/src/Icons/iconsMetaData";
import Search from "../../Search/Search";
import LegacySearch from "../../LegacySearch/LegacySearch";
import * as allIcons from "../Icons";
import Flex from "../../Flex/Flex";
import styles from "./Icon.stories.module.scss";
Expand All @@ -18,7 +18,7 @@ export default function IconsList() {

return (
<section className={styles.wrapper}>
<Search value={query} onChange={setQuery} placeholder="Search for icons" wrapperClassName={styles.search} />
<LegacySearch value={query} onChange={setQuery} placeholder="Search for icons" wrapperClassName={styles.search} />
<div className={styles.grid}>
{iconsMetaData
.filter((icon: IconMeta) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Heading from "../LegacyHeading";
import { createStoryMetaSettingsDecorator } from "../../../storybook";
import Divider from "../../Divider/Divider";
import Search from "../../Search/Search";
import LegacySearch from "../../LegacySearch/LegacySearch";
import Checkbox from "../../Checkbox/Checkbox";
import Button from "../../Button/Button";
import Flex from "../../Flex/Flex";
Expand Down Expand Up @@ -127,7 +127,7 @@ export const NotEditableHeaderOfAPage = {
<Heading type={Heading.types.h1} value="My Work" id="my-work-id" />
<Divider />
<Flex align={Flex.align.CENTER} gap={Flex.gaps.SMALL} aria-labelledby="my-work-id">
<Search wrapperClassName={styles["page-header_search"]} placeholder="Search" />
<LegacySearch wrapperClassName={styles["page-header_search"]} placeholder="Search" />
<Checkbox label="Hide done items" checked />
<Button leftIcon={Custom} kind={Button.kinds.TERTIARY}>
Customize
Expand Down
34 changes: 34 additions & 0 deletions packages/core/src/components/LegacySearch/LegacySearch.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.searchWrapper input[type="search"] {
-webkit-appearance: textfield;
}

.searchWrapper input[type="search"]::-webkit-search-decoration,
.searchWrapper input[type="search"]::-webkit-search-cancel-button,
.searchWrapper input[type="search"]::-webkit-search-results-button,
.searchWrapper input[type="search"]::-webkit-search-results-decoration {
-webkit-appearance: none;
}

.searchWrapper:focus-within .searchFocusElementFirst {
animation: dashForward 5s linear forwards;
}

.searchWrapper:focus-within .searchFocusElementSecond {
animation: dashBackwards 5s linear forwards;
}

.search.round {
border-radius: 50px !important;
}

@keyframes dashForward {
to {
stroke-dashoffset: 0;
}
}

@keyframes dashBackwards {
to {
stroke-dashoffset: 2000;
}
}
Loading

0 comments on commit 489a374

Please sign in to comment.