Skip to content

Commit

Permalink
Create input field component (#2840)
Browse files Browse the repository at this point in the history
* Created the InputField component, styled it according to the layout in figma, added it to the storybook

* changed the color of the label

* moved the component to the system design folder

* changed the height and width

* Created the InputField component, styled it according to the layout in figma, added it to the storybook

* changed the color of the label

* moved the component to the system design folder

* changed the height and width

* .

* implemented tests for the InputField component

* fixed sonnar issues

* Edited the InputField file in accordance with the comments made in the pull request, changed the path to the InputField using the appropriate alias

* rewrote class names using the cn function
  • Loading branch information
PavloButynets authored Nov 29, 2024
1 parent 0934740 commit b24422f
Show file tree
Hide file tree
Showing 5 changed files with 538 additions and 0 deletions.
228 changes: 228 additions & 0 deletions src/design-system/components/input-field/InputField.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
@use '~scss/utilities' as *;

.#{$prefix}input-container {
width: 100%;
position: relative;
display: flex;
align-items: center;
box-sizing: border-box;
background-color: get-var('neutral-0');
padding: get-var('space-0') get-var('space-1');
gap: get-var('space-1');

.#{$prefix}search-icon {
color: get-var('blue-gray-600');
font-size: get-var('font-size-xl');
}

.#{$prefix}clear-icon {
font-size: get-var('font-size-xl');
color: get-var('blue-gray-600');
cursor: pointer;
margin-left: auto;
visibility: hidden;
}

.#{$prefix}error-icon {
font-size: get-var('font-size-xl');
cursor: pointer;
margin-left: auto;
}

input:focus + .#{$prefix}clear-icon,
input:not(:placeholder-shown) ~ .#{$prefix}clear-icon {
visibility: visible;
}

input {
padding: get-var('space-1-5') get-var('space-1');
color: get-var('blue-gray-700');
background: transparent;
border: none;
outline: none;
height: 100%;
width: 100%;
font-family: get-var('font-family-rubik');

&::placeholder {
color: get-var('blue-gray-600');
font-family: get-var('font-family-rubik');
font-size: get-var('font-size-3xl');
}
}

.#{$prefix}input-label {
position: absolute;
top: 50%;
transform: translateY(-50%);
color: get-var('blue-gray-700');
background-color: get-var('neutral-0');
pointer-events: none;
transition: 0.2s ease;
padding: get-var('space-0') get-var('space-1');
}

&:has(.#{$prefix}search-icon) .#{$prefix}input-label {
left: calc(get-var('space-4') + get-var('space-0-25'));
}

input:focus + .#{$prefix}input-label,
input:not(:placeholder-shown) + .#{$prefix}input-label {
left: get-var('space-1');
top: get-var('space-0');
font-size: get-var('font-size-xs');
}

&_small {
@extend %variant-small;
}

&_large {
@extend %variant-large;
}

&_outlined {
@extend %variant-outlined;
}

&_error {
@extend %status-error;
}

&_disabled {
@extend %status-disabled;
}
}

%variant-large {
input {
font-size: get-var('font-size-3xl');
font-weight: get-var('font-weight-medium');
}

.#{$prefix}input-label {
display: none;
}
}

%variant-small {
input {
font-size: get-var('font-size-md');
font-weight: get-var('font-weight-regular');

&::placeholder {
font-size: get-var('font-size-md');
}
}

.#{$prefix}input-label {
display: none;
}
}

%variant-outlined {
outline: get-var('border-width-xs') solid get-var('blue-gray-500');
background: transparent;
border-radius: get-var('border-radius-2xs');

input {
color: get-var('blue-gray-700');
font-weight: get-var('font-weight-regular');
padding: get-var('space-2') get-var('space-1');

&::placeholder {
opacity: 0;
font-size: get-var('font-size-md');
}
}

&:hover {
outline: get-var('border-width-xs') solid get-var('blue-gray-900');
border-color: get-var('blue-gray-900');

input {
font-size: get-var('font-size-md');
color: get-var('blue-gray-800');
}

.#{$prefix}input-label {
color: get-var('blue-gray-800');
}
}

&:focus-within {
outline: get-var('border-width-md') solid get-var('blue-gray-900');

input::placeholder {
color: get-var('blue-gray-900');
font-family: get-var('font-family-rubik');
}
.#{$prefix}input-label {
color: get-var('blue-gray-800');
}
}

&.#{$prefix}input-container_error {
outline: get-var('border-width-md') solid get-var('red-500');

input {
color: get-var('red-500');
}
.#{$prefix}input-label {
color: get-var('red-500');
}
}

input:focus::placeholder {
opacity: 1;
color: get-var('blue-gray-900');
font-family: get-var('font-family-rubik');
}
}

%status-disabled {
opacity: 0.5;
pointer-events: none;
cursor: not-allowed;

+ .#{$prefix}helper-text-container {
.#{$prefix}helper-text {
opacity: 0.5;
}
}
}

.#{$prefix}helper-text-container {
height: 20px;
padding: get-var('space-0-5') get-var('space-1') get-var('space-0');

&.#{$prefix}helper-text-error {
color: get-var('red-500');
}
}

.#{$prefix}helper-text {
color: get-var('blue-gray-700');
font-size: get-var('font-size-xs');
line-height: get-var('line-height-sm');
}

.#{$prefix}helper-text-error {
color: get-var('red-500');
font-size: get-var('font-size-xs');
line-height: get-var('line-height-sm');
}

%status-error {
color: get-var('red-500');

input {
color: get-var('red-500');
}
.#{$prefix}input-label {
color: get-var('red-500');
}
.#{$prefix}helper-text {
color: get-var('red-500');
}
}
81 changes: 81 additions & 0 deletions src/design-system/components/input-field/InputField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import '~scss-components/input-field/InputField.scss'
import { InputFieldVariantEnum } from '~/types'
import SearchIcon from '@mui/icons-material/Search'
import ClearIcon from '@mui/icons-material/Clear'
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'
import { cn } from '~/utils/cn'

import { InputBaseProps } from '@mui/material/InputBase'

import Box from '@mui/material/Box'
import { SxProps } from '@mui/material'

export interface InputFieldProps extends InputBaseProps {
variant: InputFieldVariantEnum
label?: string
disabled?: boolean
value: string
placeholder: string
helperText?: string
search?: boolean
error?: boolean
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
}

const InputField: React.FC<InputFieldProps> = ({
variant = InputFieldVariantEnum.Small,
label,
disabled,
value,
placeholder,
helperText,
error,
search,
onChange,
sx
}) => {
const clearInput = () => {
onChange({ target: { value: '' } } as React.ChangeEvent<HTMLInputElement>)
}

return (
<Box sx={sx as SxProps}>
<div
className={cn('s2s-input-container', `s2s-input-container_${variant}`, {
's2s-input-container_disabled': disabled,
's2s-input-container_error': error
})}
>
{search && <SearchIcon className='s2s-search-icon' />}

<input
className='s2s-input-field'
disabled={disabled}
onChange={onChange}
placeholder={placeholder}
value={value}
/>

<label className='s2s-input-label'>{label}</label>
{error ? (
<ErrorOutlineIcon className='s2s-error-icon' />
) : (
<ClearIcon className='s2s-clear-icon' onClick={clearInput} />
)}
</div>
{helperText && (
<div className='s2s-helper-text-container'>
<span
className={cn('s2s-helper-text', {
's2s-helper-text-error': error
})}
>
{helperText}
</span>
</div>
)}
</Box>
)
}

export default InputField
Loading

0 comments on commit b24422f

Please sign in to comment.