diff --git a/.changeset/thirty-flowers-yell.md b/.changeset/thirty-flowers-yell.md new file mode 100644 index 000000000..679c735ab --- /dev/null +++ b/.changeset/thirty-flowers-yell.md @@ -0,0 +1,5 @@ +--- +"@orbit-ui/transition-components": minor +--- + +Added support for size prop on TextInput / SearchInput and PasswordInput diff --git a/packages/components/src/input/src/Input.css b/packages/components/src/input/src/Input.css index 09387109c..2ba793dcc 100644 --- a/packages/components/src/input/src/Input.css +++ b/packages/components/src/input/src/Input.css @@ -3,9 +3,12 @@ --o-ui-input-padding-with-icon: var(--hop-space-inset-xl); --o-ui-input-padding-with-button: 2.5rem; --o-ui-input-padding-loading: 2.5rem; + --o-ui-input-sm-padding: var(--hop-space-inset-squish-sm); --o-ui-input-border-color: var(--hop-neutral-border); --o-ui-input-border-radius: var(--hop-shape-rounded-md); --o-ui-input-background-color: var(--hop-neutral-surface); + --o-ui-input-height: 2.5rem; + --o-ui-input-sm-height: 2rem; } .o-ui-input { @@ -28,11 +31,15 @@ width: 100%; background-color: inherit; color: var(--hop-neutral-text); - min-height: calc(2.5rem - 2px); + min-height: calc(var(--o-ui-input-height) - 2px); margin: 0; padding: 0; } +.o-ui-input-sm input { + min-height: calc(var(--o-ui-input-sm-height) - 2px); +} + .o-ui-input textarea { width: 100%; } @@ -59,12 +66,20 @@ font-family: inherit; } +.o-ui-text-input.o-ui-input-sm { + font-size: var(--hop-body-sm-font-size); +} + .o-ui-text-input input, .o-ui-text-area textarea, .o-ui-number-input input { padding: var(--o-ui-input-padding); } +.o-ui-text-input.o-ui-input-sm input { + padding: var(--o-ui-input-sm-padding); +} + .o-ui-text-input input, .o-ui-text-area { border-radius: var(--o-ui-input-border-radius); diff --git a/packages/components/src/input/src/useInput.ts b/packages/components/src/input/src/useInput.ts index f27b9f01f..24c0d1463 100644 --- a/packages/components/src/input/src/useInput.ts +++ b/packages/components/src/input/src/useInput.ts @@ -1,5 +1,5 @@ import { ChangeEvent, ChangeEventHandler, ForwardedRef } from "react"; -import { cssModule, isNil, isNumber, mergeClasses, useAutoFocus, useEventCallback, useMergedRefs } from "../../shared"; +import { cssModule, isNil, isNumber, mergeClasses, normalizeSize, Size, useAutoFocus, useEventCallback, useMergedRefs } from "../../shared"; import { ValidationState } from "./types"; @@ -18,6 +18,7 @@ export interface UseInputProps { placeholder?: string; readOnly?: boolean; required?: boolean; + size?: Size; type?: "text" | "password" | "search" | "url" | "tel" | "email" | "number"; validationState?: ValidationState; value?: string | number; @@ -38,6 +39,7 @@ export function useInput({ placeholder, readOnly, required, + size, type, validationState, value @@ -81,7 +83,8 @@ export function useInput({ loading && "loading", active && "active", focus && "focus", - hover && "hover" + hover && "hover", + normalizeSize(size) ) ), role: "presentation" diff --git a/packages/components/src/input/src/useInputContent.tsx b/packages/components/src/input/src/useInputContent.tsx index 0689f474a..a9938cf13 100644 --- a/packages/components/src/input/src/useInputContent.tsx +++ b/packages/components/src/input/src/useInputContent.tsx @@ -1,5 +1,5 @@ import { embedIconButton } from "../../button"; -import { Spinner } from "../../spinner"; +import { Spinner, SpinnerProps } from "../../spinner"; import { CreatedIconProps } from "@hopper-ui/icons"; import { ReactElement } from "react"; import { augmentElement } from "../../shared"; @@ -21,6 +21,6 @@ export function useInputButton(button: ReactElement, isActive: boolean, props: R }); } -export function useInputSpinner(loading: boolean) { - return loading && ; +export function useInputSpinner(loading: boolean, props?: Partial) { + return loading && ; } diff --git a/packages/components/src/text-input/docs/TextInput.stories.mdx b/packages/components/src/text-input/docs/TextInput.stories.mdx index 7e2c52612..f7405ba07 100644 --- a/packages/components/src/text-input/docs/TextInput.stories.mdx +++ b/packages/components/src/text-input/docs/TextInput.stories.mdx @@ -90,6 +90,19 @@ A text input can show that it is currently loading data. +### Size + +A text input can vary in size. + + + + + + + + + + ### Fluid A text input can take the width of its container. diff --git a/packages/components/src/text-input/src/PasswordInput.tsx b/packages/components/src/text-input/src/PasswordInput.tsx index 5e9479cd4..469ac776e 100644 --- a/packages/components/src/text-input/src/PasswordInput.tsx +++ b/packages/components/src/text-input/src/PasswordInput.tsx @@ -1,7 +1,8 @@ import { AbstractTextInputProps, TextInput } from "./TextInput"; import { useState, ChangeEvent, ComponentProps, forwardRef } from "react"; import { EyeVisibleIcon, EyeHiddenIcon } from "@hopper-ui/icons"; -import { OmitInternalProps, mergeProps, useControllableState, useEventCallback } from "../../shared"; +import { OmitInternalProps, mergeProps, createSizeAdapter, useControllableState, useEventCallback } from "../../shared"; +import { useResponsiveValue } from "../../styling"; import { IconButton } from "../../button"; import { useInputGroupTextInputProps } from "../../input-group"; @@ -10,6 +11,13 @@ const DefaultElement = "input"; export type InnerPasswordInputProps = AbstractTextInputProps; +/* eslint-disable sort-keys, sort-keys-fix/sort-keys-fix */ +const buttonSize = createSizeAdapter({ + "sm": "xs", + "md": "sm" +}); +/* eslint-enable sort-keys, sort-keys-fix/sort-keys-fix */ + export function InnerPasswordInput(props: InnerPasswordInputProps) { const [inputGroupProps] = useInputGroupTextInputProps(); @@ -17,6 +25,7 @@ export function InnerPasswordInput(props: InnerPasswordInputProps) { as = DefaultElement, defaultValue, forwardedRef, + size, value, wrapperProps, ...rest @@ -28,6 +37,8 @@ export function InnerPasswordInput(props: InnerPasswordInputProps) { const [inputValue, setValue] = useControllableState(value, defaultValue, ""); const [isHidden, setIsHidden] = useState(true); + const sizeValue = useResponsiveValue(size); + const handleChange = useEventCallback((event: ChangeEvent) => { setValue(event.target.value); }); @@ -42,7 +53,7 @@ export function InnerPasswordInput(props: InnerPasswordInputProps) { aria-label="Toggle password visibility" className="o-ui-password-input-show-button" onClick={handleShowValue} - size="sm" + size={buttonSize(sizeValue)} title="Toggle password visibility" variant="tertiary" > @@ -59,6 +70,7 @@ export function InnerPasswordInput(props: InnerPasswordInputProps) { button: showButtonMarkup, onChange: handleChange, ref: forwardedRef, + size: size, type: isHidden ? "password" as const : "text" as const, value: inputValue, wrapperProps: mergeProps(wrapperProps ?? {}, { diff --git a/packages/components/src/text-input/src/SearchInput.tsx b/packages/components/src/text-input/src/SearchInput.tsx index cff24fb83..b648dd4ad 100644 --- a/packages/components/src/text-input/src/SearchInput.tsx +++ b/packages/components/src/text-input/src/SearchInput.tsx @@ -40,6 +40,7 @@ export function InnerSearchInput(props: InnerSearchInputProps) { onChange, onKeyDown, onValueChange, + size, value, wrapperProps, ...rest @@ -111,6 +112,7 @@ export function InnerSearchInput(props: InnerSearchInputProps) { onChange: handleChange, onKeyDown: handleKeyDown, ref: inputRef, + size: size, spellCheck: "false", type: "search" as const, value: inputValue, diff --git a/packages/components/src/text-input/src/TextInput.tsx b/packages/components/src/text-input/src/TextInput.tsx index 6f58834c7..5e8913b7a 100644 --- a/packages/components/src/text-input/src/TextInput.tsx +++ b/packages/components/src/text-input/src/TextInput.tsx @@ -1,8 +1,8 @@ -import { AbstractInputProps, adaptInputStylingProps, useInput, useInputButton, useInputHasFocus, useInputIcon, useInputSpinner } from "../../input"; +import { AbstractInputProps, adaptInputStylingProps, useInput, useInputIcon, useInputSpinner, useInputButton, useInputHasFocus } from "../../input"; import { Box, BoxProps } from "../../box"; import { ChangeEvent, ComponentProps, ElementType, ReactElement, forwardRef } from "react"; import { ClearInputGroupContext, useInputGroupTextInputProps } from "../../input-group"; -import { OmitInternalProps, cssModule, isNil, mergeProps, omitProps, useChainedEventCallback, useControllableState } from "../../shared"; +import { OmitInternalProps, cssModule, createSizeAdapter, isNil, mergeProps, omitProps, useChainedEventCallback, useControllableState } from "../../shared"; import { ResponsiveProp, useResponsiveValue } from "../../styling"; import { useFieldInputProps } from "../../field"; import { useToolbarProps } from "../../toolbar"; @@ -35,6 +35,10 @@ export type AbstractTextInputProps = AbstractInputProps, value: string) => void; + /** + * An input can vary in size. + */ + size?: ResponsiveProp<"sm" | "md">; /** * A controlled value. */ @@ -54,6 +58,20 @@ export interface InnerTextInputProps extends AbstractTextInputProps @@ -189,7 +212,6 @@ export function InnerTextInput(props: InnerTextInputProps) { ); } -InnerTextInput.defaultElement = DefaultElement; /** * A text input allow a user to enter and edit a text. diff --git a/packages/components/src/text-input/tests/chromatic/PasswordInput.stories.tsx b/packages/components/src/text-input/tests/chromatic/PasswordInput.stories.tsx index 83fbbf3f7..3dddf4b18 100644 --- a/packages/components/src/text-input/tests/chromatic/PasswordInput.stories.tsx +++ b/packages/components/src/text-input/tests/chromatic/PasswordInput.stories.tsx @@ -14,7 +14,10 @@ export const Default: PasswordInputStory = { storyName: "default", render: () => ( - + + + + @@ -29,7 +32,10 @@ export const Placeholder: PasswordInputStory = { storyName: "placeholder", render: () => ( - + + + + @@ -44,7 +50,10 @@ export const Value: PasswordInputStory = { storyName: "value", render: () => ( - + + + + @@ -87,10 +96,16 @@ export const Zoom: PasswordInputStory = { render: () => (
- + + + +
- + + + +
) diff --git a/packages/components/src/text-input/tests/chromatic/SearchInput.stories.tsx b/packages/components/src/text-input/tests/chromatic/SearchInput.stories.tsx index 257a2615c..8ed0b345f 100644 --- a/packages/components/src/text-input/tests/chromatic/SearchInput.stories.tsx +++ b/packages/components/src/text-input/tests/chromatic/SearchInput.stories.tsx @@ -16,8 +16,14 @@ export const Default: SearchInputStory = { storyName: "default", render: () => ( - - + + + + + + + + @@ -25,7 +31,7 @@ export const Default: SearchInputStory = { - +
) }; @@ -33,7 +39,10 @@ export const Placeholder: SearchInputStory = { storyName: "placeholder", render: () => ( - + + + + @@ -50,7 +59,10 @@ export const Value: SearchInputStory = { storyName: "value", render: () => ( - + + + + @@ -71,7 +83,10 @@ export const CustomIcon: SearchInputStory = { storyName: "custom icon", render: () => ( - } placeholder="Where to?" aria-label="Label" /> + + } placeholder="Where to?" aria-label="Label" /> + } placeholder="Where to?" aria-label="Label" size="sm" /> + } defaultValue="SpaceX will win the race!" aria-label="Label" /> } placeholder="Where to?" /> } placeholder="Where to?" /> @@ -132,10 +147,16 @@ export const Zoom: SearchInputStory = { render: () => (
- + + + +
- + + + +
) diff --git a/packages/components/src/text-input/tests/chromatic/TextInput.stories.tsx b/packages/components/src/text-input/tests/chromatic/TextInput.stories.tsx index 3783161be..e66b57352 100644 --- a/packages/components/src/text-input/tests/chromatic/TextInput.stories.tsx +++ b/packages/components/src/text-input/tests/chromatic/TextInput.stories.tsx @@ -17,9 +17,15 @@ export const Default: TextInputStory = { storyName: "default", render: () => ( - + + + + - + + + + @@ -35,7 +41,10 @@ export const Placeholder: TextInputStory = { storyName: "placeholder", render: () => ( - + + + + @@ -53,7 +62,10 @@ export const Value: TextInputStory = { storyName: "value", render: () => ( - + + + + @@ -75,7 +87,10 @@ export const Icon: TextInputStory = { storyName: "icon", render: () => ( - } placeholder="Where to?" aria-label="Label" /> + + } placeholder="Where to?" aria-label="Label" /> + } placeholder="Where to?" aria-label="Label" size="sm" /> + } defaultValue="SpaceX will win the race!" placeholder="Where to?" /> } placeholder="Where to?" /> } placeholder="Where to?" /> @@ -93,7 +108,10 @@ export const Button: TextInputStory = { storyName: "button", render: () => ( - } placeholder="Where to?" /> + + } placeholder="Where to?" /> + } placeholder="Where to?" size="sm" /> + } placeholder="Where to?" defaultValue="SpaceX will win the race!" /> } placeholder="Where to?" /> @@ -153,10 +171,16 @@ export const Zoom: TextInputStory = { render: () => (
- + + + +
- + + + +
)