-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
coderwelsch
committed
Nov 6, 2023
1 parent
35446b6
commit 4134a1e
Showing
3 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
src/components/form-field/search-field/search-field.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/* eslint-disable react/jsx-props-no-spreading */ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import React, { useState } from "react"; | ||
import { FormField } from "../form-field"; | ||
|
||
const meta: Meta<typeof FormField.SearchInput> = { | ||
title: "Input/SearchInput", | ||
component: FormField.SearchInput, | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof FormField.SearchInput>; | ||
|
||
const SearchInputWithHooks = ({ | ||
error = false, | ||
disabled = false, | ||
readOnly = false, | ||
value, | ||
}: { | ||
error?: boolean; | ||
disabled?: boolean; | ||
readOnly?: boolean; | ||
value?: string; | ||
}) => { | ||
const [inputValue, setInputValue] = useState(value); | ||
|
||
return ( | ||
<FormField> | ||
<FormField.LabelGroup> | ||
<FormField.Label htmlFor="value">Label</FormField.Label> | ||
<FormField.Description id="value-description">Description</FormField.Description> | ||
</FormField.LabelGroup> | ||
<FormField.SearchInput | ||
id="value" | ||
value={inputValue} | ||
onChange={(e) => setInputValue(e.target.value)} | ||
ariaDescribedBy="value-description" | ||
error={error} | ||
disabled={disabled} | ||
readOnly={readOnly} | ||
onClear={() => { | ||
setInputValue(""); | ||
}} | ||
/> | ||
{error ? <FormField.ErrorMessage>Error message.</FormField.ErrorMessage> : null} | ||
</FormField> | ||
); | ||
}; | ||
|
||
export const Default: Story = { | ||
render: () => ( | ||
<div className="w-72"> | ||
<SearchInputWithHooks /> | ||
</div> | ||
), | ||
}; | ||
|
||
export const WithError: Story = { | ||
render: () => ( | ||
<div className="w-72"> | ||
<SearchInputWithHooks error /> | ||
</div> | ||
), | ||
}; | ||
|
||
export const ReadOnly: Story = { | ||
render: () => ( | ||
<div className="w-72"> | ||
<SearchInputWithHooks readOnly value="Readonly text" /> | ||
</div> | ||
), | ||
}; | ||
|
||
export const Disabled: Story = { | ||
render: () => ( | ||
<div className="w-72"> | ||
<SearchInputWithHooks disabled /> | ||
</div> | ||
), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import React, { useMemo, useRef } from "react"; | ||
import { classNames } from "../../../util/class-names"; | ||
import CrossIcon from "../../../icons/cross-icon"; | ||
import SearchIcon from "../../../icons/search-icon"; | ||
|
||
export interface SearchInputProps extends React.ComponentPropsWithoutRef<"input"> { | ||
autoSelect?: boolean; | ||
ariaDescribedBy?: string; | ||
error?: boolean; | ||
onClear: () => void; | ||
} | ||
|
||
export const SearchInput = ({ | ||
ariaDescribedBy, | ||
readOnly, | ||
autoSelect, | ||
onClear, | ||
error, | ||
value, | ||
disabled, | ||
className, | ||
...props | ||
}: SearchInputProps) => { | ||
const inputRef = useRef<HTMLInputElement>(null); | ||
|
||
const isClearIconShown = useMemo(() => { | ||
return !readOnly && !disabled && value?.toString().length; | ||
}, [disabled, readOnly, value]); | ||
|
||
const handleAutoSelection = () => { | ||
if (autoSelect && inputRef.current) { | ||
inputRef.current.select(); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className={classNames("relative w-full")}> | ||
<div | ||
className="pointer-events-none absolute inset-y-0 left-0 z-10 flex items-center pl-3" | ||
aria-hidden="true" | ||
> | ||
<SearchIcon className="text-gray-400 h-3.5 w-3.5 fill-neutral-600" /> | ||
</div> | ||
|
||
<input | ||
ref={inputRef} | ||
aria-describedby={ariaDescribedBy} | ||
onMouseOver={handleAutoSelection} | ||
onFocus={handleAutoSelection} | ||
onClick={handleAutoSelection} | ||
type="search" | ||
className={classNames( | ||
"paragraph-100 relative block h-8 w-full rounded border border-neutral-400 py-2 px-9 text-neutral-800 placeholder:text-neutral-600 focus:outline-none", | ||
readOnly && "bg-neutral-100", | ||
disabled && "cursor-not-allowed bg-neutral-100 text-neutral-600", | ||
!error && | ||
!disabled && | ||
"hover:border-neutral-600 focus:border-primary-400 focus:ring-2 focus:ring-primary-200", | ||
error && !disabled && "border-danger-500", | ||
className | ||
)} | ||
readOnly={readOnly} | ||
disabled={disabled} | ||
value={value} | ||
{...props} | ||
/> | ||
|
||
{isClearIconShown ? ( | ||
<div | ||
tabIndex={0} | ||
onClick={onClear} | ||
onKeyDown={(e) => { | ||
if (e.key === "Enter" || e.key === " ") { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
|
||
onClear(); | ||
} | ||
}} | ||
className="absolute top-1/2 transform -translate-y-1/2 right-0 z-10 flex items-center justify-center bg-neutral-100 border border-transparent hover:border-neutral-300 mr-1.5 w-5 h-5 rounded cursor-pointer" | ||
Check failure on line 80 in src/components/form-field/search-field/search-field.tsx GitHub Actions / format-check
|
||
aria-label="Clear Search Input" | ||
role="button" | ||
> | ||
<CrossIcon className="h-3.5 w-3.5 fill-neutral-600" /> | ||
</div> | ||
) : null} | ||
</div> | ||
); | ||
}; |