Skip to content

Commit

Permalink
Merge pull request #1053 from glints-dev/feature/text-field-required-…
Browse files Browse the repository at this point in the history
…input

Adding floating placeholder and required asterisk symbol
  • Loading branch information
Tai-DucTran authored Sep 11, 2024
2 parents 470418a + 0f28229 commit dc47a0e
Show file tree
Hide file tree
Showing 45 changed files with 697 additions and 57 deletions.
53 changes: 49 additions & 4 deletions src/@next/Input/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useEffect, useRef } from 'react';
import {
AsteriskIcon,
FloatingLabel,
StyledContainer,
StyledInput,
StyledPrefixContainer,
Expand All @@ -9,14 +11,34 @@ import {
export interface InputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
error?: boolean;
height?: string;
border?: string;
borderRadius?: string;
floatingFontSize?: string;
isPlaceholderFloating?: boolean;
required?: boolean;
prefix?: React.ReactNode;
suffix?: React.ReactNode;
inputRef?: React.Ref<HTMLInputElement>;
}

export const Input = React.forwardRef<HTMLInputElement, InputProps>(
function Input(
{ error, disabled, prefix, suffix, inputRef, ...props }: InputProps,
{
error,
disabled,
prefix,
suffix,
inputRef,
height,
border,
borderRadius,
floatingFontSize,
isPlaceholderFloating,
placeholder,
required,
...props
}: InputProps,
ref
) {
const hasPrefix = !!prefix;
Expand All @@ -27,12 +49,16 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(

const Prefix = () =>
hasPrefix ? (
<StyledPrefixContainer ref={prefixRef}>{prefix}</StyledPrefixContainer>
<StyledPrefixContainer ref={prefixRef} height={height}>
{prefix}
</StyledPrefixContainer>
) : null;

const Suffix = () =>
hasSuffix ? (
<StyledSuffixContainer ref={suffixRef}>{suffix}</StyledSuffixContainer>
<StyledSuffixContainer ref={suffixRef} height={height}>
{suffix}
</StyledSuffixContainer>
) : null;

const [prefixWidth, setPrefixWidth] = React.useState(0);
Expand All @@ -52,6 +78,8 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
}
}, [hasSuffix, suffix]);

const selectedPlaceholder = required ? `${placeholder}*` : placeholder;

return (
<StyledContainer
ref={ref}
Expand All @@ -63,7 +91,24 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
suffixWidth={suffixWidth}
>
<Prefix />
<StyledInput ref={inputRef} disabled={disabled} {...props} />
<StyledInput
ref={inputRef}
placeholder={isPlaceholderFloating ? undefined : selectedPlaceholder}
borderRadius={borderRadius}
border={border}
height={height}
disabled={disabled}
{...props}
/>
{isPlaceholderFloating && (
<FloatingLabel
data-testid="textarea-label"
fontSize={floatingFontSize}
>
{placeholder}
{required && <AsteriskIcon>*</AsteriskIcon>}
</FloatingLabel>
)}
<Suffix />
</StyledContainer>
);
Expand Down
38 changes: 32 additions & 6 deletions src/@next/Input/InputStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { InputProps } from './Input';
import { space12, space4, space8 } from '../utilities/spacing';
import { NotoSans } from '../utilities/fonts';
import { borderRadius4 } from '../utilities/borderRadius';
import { Greyscale } from '../../Utils/Colors';

export interface PreffixSuffixWidthProps {
prefixWidth: number;
Expand Down Expand Up @@ -56,13 +57,15 @@ export const StyledContainer = styled.div<InputProps & PreffixSuffixWidthProps>`
}
`;

export const StyledPrefixContainer = styled.div`
export const StyledPrefixContainer = styled.div<{
height?: string;
}>`
position: absolute;
left: 0;
color: ${Neutral.B40};
display: flex;
align-items: center;
height: 36px;
height: ${({ height }) => height ?? '36px'};
padding: 0px ${space8} 0 ${space12};
svg {
Expand All @@ -72,18 +75,35 @@ export const StyledPrefixContainer = styled.div`
}
`;

export const StyledSuffixContainer = styled(StyledPrefixContainer)`
export const StyledSuffixContainer = styled(StyledPrefixContainer)<{
height?: string;
}>`
left: auto;
right: 0;
padding: 0px ${space12} 0 ${space4};
`;

export const FloatingLabel = styled.label<{
top?: number;
left?: number;
fontSize?: string;
}>`
position: absolute;
left: ${({ left }) => left ?? '0.5'}em;
top: ${({ top }) => top ?? '-1.25'}em;
visibility: visible;
padding: 0 0.3em;
background: white;
font-size: ${({ fontSize }) => fontSize ?? '12px'};
color: ${Greyscale.devilsgrey};
`;

export const StyledInput = styled.input<InputProps>`
background: ${Neutral.B100};
box-sizing: border-box;
border: 1px solid ${Neutral.B68};
border-radius: ${borderRadius4};
border: ${({ border }) => border ?? `1px solid ${Neutral.B68}`};
border-radius: ${({ borderRadius }) => borderRadius ?? borderRadius4};
padding: 0 12px;
font-family: ${NotoSans}, sans-serif;
Expand All @@ -98,7 +118,7 @@ export const StyledInput = styled.input<InputProps>`
order: 1;
align-self: stretch;
flex-grow: 0;
height: 36px;
height: ${({ height }) => height ?? `36px`};
&::placeholder {
color: ${Neutral.B68};
Expand All @@ -115,3 +135,9 @@ export const StyledInput = styled.input<InputProps>`
font-size: 14px;
}
`;

export const AsteriskIcon = styled.span`
color: ${Red.B93};
font-size: 20px;
vertical-align: middle;
`;
30 changes: 27 additions & 3 deletions src/@next/NumberInput/NumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import React from 'react';
import styled from 'styled-components';
import { Input, InputProps } from '../Input/Input';

export type NumberInputProps = Omit<InputProps, 'type' | 'prefix'>;
export type NumberInputProps = Omit<InputProps, 'type' | 'prefix'> & {
border?: string;
borderRadius?: string;
required?: boolean;
isPlaceholderFloating?: boolean;
};

const StyledInput = styled(Input)`
&[type='number'] {
Expand All @@ -17,7 +22,26 @@ const StyledInput = styled(Input)`
`;

export const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
function NumberInput(props: NumberInputProps, ref) {
return <StyledInput ref={ref} type="number" {...props} />;
function NumberInput(
{
border,
borderRadius,
isPlaceholderFloating,
required,
...props
}: NumberInputProps,
ref
) {
return (
<StyledInput
ref={ref}
type="number"
{...props}
border={border}
borderRadius={borderRadius}
required={required}
isPlaceholderFloating={isPlaceholderFloating}
/>
);
}
);
22 changes: 21 additions & 1 deletion src/@next/PasswordInput/PasswordInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,28 @@ export type PasswordInputProps = Omit<
> & {
onChange?: (value: string) => void;
error?: boolean;
height?: string;
border?: string;
borderRadius?: string;
required?: boolean;
isPlaceholderFloating?: boolean;
};

export const PasswordInput = React.forwardRef<
HTMLInputElement,
PasswordInputProps
>(function PasswordInput(
{ value, onChange, error, ...props }: PasswordInputProps,
{
value,
onChange,
error,
height,
border,
borderRadius,
required,
isPlaceholderFloating,
...props
}: PasswordInputProps,
ref
) {
const [showPassword, setShowPassword] = useState<boolean>(false);
Expand Down Expand Up @@ -44,6 +59,11 @@ export const PasswordInput = React.forwardRef<
onChange={e => onChange?.(e.currentTarget.value)}
error={error}
suffix={!props.disabled && suffixComponent}
height={height}
border={border}
borderRadius={borderRadius}
required={required}
isPlaceholderFloating={isPlaceholderFloating}
{...props}
/>
</>
Expand Down
Loading

0 comments on commit dc47a0e

Please sign in to comment.