Replace hotkey scopes by focus stack (Part 4 - Inputs) (#12933)

# Replace hotkey scopes by focus stack (Part 4 - Inputs)

This PR is the 4th part of a refactoring aiming to deprecate the hotkey
scopes api in favor of the new focus stack api which is more robust.
Part 1: https://github.com/twentyhq/twenty/pull/12673
Part 2: https://github.com/twentyhq/twenty/pull/12798
Part 3: https://github.com/twentyhq/twenty/pull/12910

In this part, I refactored all inputs in the app so that each input has
a unique id which can be used to track the focused element.
This commit is contained in:
Raphaël Bosi
2025-07-07 15:42:12 +02:00
committed by GitHub
parent 0199d5f72a
commit c6e5bab4e9
140 changed files with 1178 additions and 1004 deletions
@@ -1,271 +0,0 @@
import styled from '@emotion/styled';
import { useRef, useState } from 'react';
import { HotkeysEvent } from 'react-hotkeys-hook/dist/types';
import TextareaAutosize from 'react-textarea-autosize';
import { Key } from 'ts-key-enum';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { IconArrowRight } from 'twenty-ui/display';
import { Button, RoundedIconButton } from 'twenty-ui/input';
import { InputHotkeyScope } from '../types/InputHotkeyScope';
const MAX_ROWS = 5;
export enum AutosizeTextInputVariant {
Default = 'default',
Icon = 'icon',
Button = 'button',
}
type AutosizeTextInputProps = {
onValidate?: (text: string) => void;
minRows?: number;
placeholder?: string;
onFocus?: () => void;
variant?: AutosizeTextInputVariant;
buttonTitle?: string;
value?: string;
className?: string;
onBlur?: () => void;
autoFocus?: boolean;
disabled?: boolean;
};
const StyledContainer = styled.div`
width: 100%;
`;
const StyledInputContainer = styled.div`
display: flex;
position: relative;
width: 100%;
`;
type StyledTextAreaProps = {
variant: AutosizeTextInputVariant;
};
const StyledTextArea = styled(TextareaAutosize)<StyledTextAreaProps>`
background: ${({ theme, variant }) =>
variant === AutosizeTextInputVariant.Button
? 'transparent'
: theme.background.tertiary};
border: none;
border-radius: 5px;
color: ${({ theme }) => theme.font.color.primary};
font-family: inherit;
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.regular};
line-height: 16px;
overflow: auto;
&:focus {
border: none;
outline: none;
}
&::placeholder {
color: ${({ theme }) => theme.font.color.light};
font-weight: ${({ theme }) => theme.font.weight.regular};
}
padding: ${({ variant }) =>
variant === AutosizeTextInputVariant.Button ? '8px 0' : '8px'};
resize: none;
width: 100%;
`;
// TODO: this messes with the layout, fix it
const StyledBottomRightRoundedIconButton = styled.div`
height: 0;
position: relative;
right: 26px;
top: 6px;
width: 0px;
`;
const StyledSendButton = styled(Button)`
margin-left: ${({ theme }) => theme.spacing(2)};
`;
const StyledWordCounter = styled.div`
color: ${({ theme }) => theme.font.color.light};
font-weight: ${({ theme }) => theme.font.weight.medium};
line-height: 150%;
width: 100%;
`;
type StyledBottomContainerProps = {
isTextAreaHidden: boolean;
};
const StyledBottomContainer = styled.div<StyledBottomContainerProps>`
align-items: center;
display: flex;
justify-content: space-between;
margin-top: ${({ theme, isTextAreaHidden }) =>
isTextAreaHidden ? 0 : theme.spacing(4)};
`;
const StyledCommentText = styled.div`
cursor: text;
padding-bottom: ${({ theme }) => theme.spacing(1)};
padding-top: ${({ theme }) => theme.spacing(1)};
`;
export const AutosizeTextInput = ({
placeholder,
onValidate,
minRows = 1,
onFocus,
variant = AutosizeTextInputVariant.Default,
buttonTitle,
value = '',
className,
onBlur,
autoFocus,
disabled,
}: AutosizeTextInputProps) => {
const [isFocused, setIsFocused] = useState(false);
const [isHidden, setIsHidden] = useState(
variant === AutosizeTextInputVariant.Button,
);
const [text, setText] = useState(value);
const textInputRef = useRef<HTMLTextAreaElement>(null);
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
const isSendButtonDisabled = !text;
const words = text.split(/\s|\n/).filter((word) => word).length;
useScopedHotkeys(
['shift+enter', 'enter'],
(event: KeyboardEvent, handler: HotkeysEvent) => {
if (handler.shift || !isFocused) {
return;
} else {
event.preventDefault();
onValidate?.(text);
setText('');
}
},
InputHotkeyScope.TextInput,
[onValidate, text, setText, isFocused],
{
enableOnContentEditable: true,
enableOnFormTags: true,
},
);
useScopedHotkeys(
Key.Escape,
(event: KeyboardEvent) => {
if (!isFocused) {
return;
}
event.preventDefault();
setText('');
goBackToPreviousHotkeyScope();
textInputRef.current?.blur();
},
InputHotkeyScope.TextInput,
[onValidate, setText, isFocused],
{
enableOnContentEditable: true,
enableOnFormTags: true,
},
);
const handleInputChange = (event: React.FormEvent<HTMLTextAreaElement>) => {
const newText = event.currentTarget.value;
setText(newText);
};
const handleOnClickSendButton = () => {
onValidate?.(text);
setText('');
};
const handleFocus = () => {
onFocus?.();
setIsFocused(true);
setHotkeyScopeAndMemorizePreviousScope({
scope: InputHotkeyScope.TextInput,
});
};
const handleBlur = () => {
onBlur?.();
setIsFocused(false);
goBackToPreviousHotkeyScope();
};
const computedMinRows = minRows > MAX_ROWS ? MAX_ROWS : minRows;
return (
<>
<StyledContainer className={className}>
<StyledInputContainer>
{!isHidden && (
<StyledTextArea
ref={textInputRef}
autoFocus={
autoFocus || variant === AutosizeTextInputVariant.Button
}
placeholder={placeholder ?? 'Write a comment'}
maxRows={MAX_ROWS}
minRows={computedMinRows}
onChange={handleInputChange}
value={text}
onFocus={handleFocus}
onBlur={handleBlur}
variant={variant}
disabled={disabled}
/>
)}
{variant === AutosizeTextInputVariant.Icon && (
<StyledBottomRightRoundedIconButton>
<RoundedIconButton
onClick={handleOnClickSendButton}
Icon={IconArrowRight}
disabled={isSendButtonDisabled}
/>
</StyledBottomRightRoundedIconButton>
)}
</StyledInputContainer>
{variant === AutosizeTextInputVariant.Button && (
<StyledBottomContainer isTextAreaHidden={isHidden}>
<StyledWordCounter>
{isHidden ? (
<StyledCommentText
onClick={() => {
setIsHidden(false);
onFocus?.();
}}
>
Write a comment
</StyledCommentText>
) : (
`${words} word${words === 1 ? '' : 's'}`
)}
</StyledWordCounter>
<StyledSendButton
title={buttonTitle ?? 'Comment'}
disabled={isSendButtonDisabled}
onClick={handleOnClickSendButton}
/>
</StyledBottomContainer>
)}
</StyledContainer>
</>
);
};
@@ -6,7 +6,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { arrayToChunks } from '~/utils/array/arrayToChunks';
import { ICON_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/ui/input/components/constants/IconPickerDropdownContentWidth';
@@ -14,7 +13,7 @@ import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
import { useSelectableListListenToEnterHotkeyOnItem } from '@/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { t } from '@lingui/core/macro';
@@ -25,7 +24,6 @@ import {
IconButtonVariant,
LightIconButton,
} from 'twenty-ui/input';
import { IconPickerHotkeyScope } from '../types/IconPickerHotkeyScope';
export type IconPickerProps = {
disabled?: boolean;
@@ -51,9 +49,16 @@ const StyledMenuIconItemsContainer = styled.div`
gap: ${({ theme }) => theme.spacing(0.5)};
`;
const StyledLightIconButton = styled(LightIconButton)<{ isSelected?: boolean }>`
background: ${({ theme, isSelected }) =>
isSelected ? theme.background.transparent.medium : 'transparent'};
const StyledLightIconButton = styled(LightIconButton)<{
isSelected?: boolean;
isFocused?: boolean;
}>`
background: ${({ theme, isSelected, isFocused }) =>
isSelected
? theme.background.transparent.medium
: isFocused
? theme.background.transparent.light
: 'transparent'};
`;
const convertIconKeyToLabel = (iconKey: string) =>
@@ -64,6 +69,7 @@ type IconPickerIconProps = {
onClick: () => void;
selectedIconKey?: string;
Icon: IconComponent;
focusedIconKey?: string;
};
const IconPickerIcon = ({
@@ -71,29 +77,26 @@ const IconPickerIcon = ({
onClick,
selectedIconKey,
Icon,
focusedIconKey,
}: IconPickerIconProps) => {
const isSelectedItemId = useRecoilComponentValueV2(
selectedItemIdComponentState,
iconKey,
);
useSelectableListListenToEnterHotkeyOnItem({
focusId: iconKey,
itemId: iconKey,
onEnter: onClick,
hotkeyScope: DropdownHotkeyScope.Dropdown,
});
return (
<StyledLightIconButton
key={iconKey}
aria-label={convertIconKeyToLabel(iconKey)}
size="medium"
title={iconKey}
isSelected={iconKey === selectedIconKey || !!isSelectedItemId}
Icon={Icon}
onClick={onClick}
/>
<SelectableListItem itemId={iconKey} onEnter={onClick}>
<StyledLightIconButton
key={iconKey}
aria-label={convertIconKeyToLabel(iconKey)}
size="medium"
title={iconKey}
isSelected={iconKey === selectedIconKey || !!isSelectedItemId}
isFocused={iconKey === focusedIconKey}
Icon={Icon}
onClick={onClick}
/>
</SelectableListItem>
);
};
@@ -114,26 +117,18 @@ export const IconPicker = ({
maxIconsVisible = 25,
}: IconPickerProps) => {
const [searchString, setSearchString] = useState('');
const {
goBackToPreviousHotkeyScope,
setHotkeyScopeAndMemorizePreviousScope,
} = usePreviousHotkeyScope();
const [isMouseInsideIconList, setIsMouseInsideIconList] = useState(false);
const handleMouseEnter = () => {
if (!isMouseInsideIconList) {
setIsMouseInsideIconList(true);
setHotkeyScopeAndMemorizePreviousScope({
scope: IconPickerHotkeyScope.IconPicker,
});
}
};
const handleMouseLeave = () => {
if (isMouseInsideIconList) {
setIsMouseInsideIconList(false);
goBackToPreviousHotkeyScope();
}
};
@@ -192,6 +187,14 @@ export const IconPicker = ({
const icon = selectedIconKey ? getIcon(selectedIconKey) : IconApps;
const selectableListInstanceId = 'icon-list';
const focusedIconKey =
useRecoilComponentValueV2(
selectedItemIdComponentState,
selectableListInstanceId,
) ?? undefined;
return (
<div className={className}>
<Dropdown
@@ -217,7 +220,7 @@ export const IconPicker = ({
widthInPixels={dropdownWidth || ICON_PICKER_DROPDOWN_CONTENT_WIDTH}
>
<SelectableList
selectableListInstanceId="icon-list"
selectableListInstanceId={selectableListInstanceId}
selectableItemIdMatrix={iconKeys2d}
focusId={dropdownId}
hotkeyScope={DropdownHotkeyScope.Dropdown}
@@ -246,6 +249,7 @@ export const IconPicker = ({
}}
selectedIconKey={selectedIconKey}
Icon={getIcon(iconKey)}
focusedIconKey={focusedIconKey}
/>
))}
</StyledMenuIconItemsContainer>
@@ -2,14 +2,17 @@ import styled from '@emotion/styled';
import { FocusEventHandler, useId } from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { InputHotkeyScope } from '@/ui/input/types/InputHotkeyScope';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { RGBA } from 'twenty-ui/theme';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
import { InputHotkeyScope } from '../types/InputHotkeyScope';
const MAX_ROWS = 5;
export type TextAreaProps = {
textAreaId: string;
label?: string;
disabled?: boolean;
minRows?: number;
@@ -69,6 +72,7 @@ const StyledTextArea = styled(TextareaAutosize)`
`;
export const TextArea = ({
textAreaId,
label,
disabled,
placeholder,
@@ -81,30 +85,37 @@ export const TextArea = ({
}: TextAreaProps) => {
const computedMinRows = Math.min(minRows, maxRows);
const inputId = useId();
const instanceId = useId();
const {
goBackToPreviousHotkeyScope,
setHotkeyScopeAndMemorizePreviousScope,
} = usePreviousHotkeyScope();
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById();
const handleFocus: FocusEventHandler<HTMLTextAreaElement> = () => {
setHotkeyScopeAndMemorizePreviousScope({
scope: InputHotkeyScope.TextInput,
pushFocusItemToFocusStack({
focusId: textAreaId,
component: {
type: FocusComponentType.TEXT_AREA,
instanceId: textAreaId,
},
globalHotkeysConfig: {
enableGlobalHotkeysConflictingWithKeyboard: false,
},
hotkeyScope: { scope: InputHotkeyScope.TextInput },
});
};
const handleBlur: FocusEventHandler<HTMLTextAreaElement> = () => {
goBackToPreviousHotkeyScope();
removeFocusItemFromFocusStackById({ focusId: textAreaId });
onBlur?.();
};
return (
<StyledContainer>
{label && <StyledLabel htmlFor={inputId}>{label}</StyledLabel>}
{label && <StyledLabel htmlFor={instanceId}>{label}</StyledLabel>}
<StyledTextArea
id={inputId}
id={instanceId}
placeholder={placeholder}
maxRows={maxRows}
minRows={computedMinRows}
@@ -6,11 +6,14 @@ import {
TextInputV2ComponentProps,
} from '@/ui/input/components/TextInputV2';
import { InputHotkeyScope } from '@/ui/input/types/InputHotkeyScope';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import { isDefined } from 'twenty-shared/utils';
export type TextInputProps = TextInputV2ComponentProps & {
instanceId: string;
disableHotkeys?: boolean;
onInputEnter?: () => void;
dataTestId?: string;
@@ -19,6 +22,7 @@ export type TextInputProps = TextInputV2ComponentProps & {
};
export const TextInput = ({
instanceId,
onFocus,
onBlur,
onInputEnter,
@@ -45,18 +49,25 @@ export const TextInput = ({
}
}, [autoSelectOnMount]);
const {
goBackToPreviousHotkeyScope,
setHotkeyScopeAndMemorizePreviousScope,
} = usePreviousHotkeyScope();
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById();
const handleFocus: FocusEventHandler<HTMLInputElement> = (e) => {
onFocus?.(e);
setIsFocused(true);
if (!disableHotkeys) {
setHotkeyScopeAndMemorizePreviousScope({
scope: InputHotkeyScope.TextInput,
pushFocusItemToFocusStack({
focusId: instanceId,
component: {
type: FocusComponentType.TEXT_INPUT,
instanceId: instanceId,
},
globalHotkeysConfig: {
enableGlobalHotkeysConflictingWithKeyboard: false,
},
hotkeyScope: { scope: InputHotkeyScope.TextInput },
});
}
};
@@ -66,48 +77,51 @@ export const TextInput = ({
setIsFocused(false);
if (!disableHotkeys) {
goBackToPreviousHotkeyScope();
removeFocusItemFromFocusStackById({ focusId: instanceId });
}
};
useScopedHotkeys(
[Key.Escape],
() => {
if (!isFocused) {
return;
}
const handleEscape = () => {
if (!isFocused) {
return;
}
if (isDefined(inputRef) && 'current' in inputRef) {
inputRef.current?.blur();
setIsFocused(false);
}
};
if (isDefined(inputRef) && 'current' in inputRef) {
inputRef.current?.blur();
setIsFocused(false);
}
},
InputHotkeyScope.TextInput,
[inputRef, isFocused],
{
const handleEnter = () => {
if (!isFocused) {
return;
}
onInputEnter?.();
if (isDefined(inputRef) && 'current' in inputRef) {
setIsFocused(false);
}
};
useHotkeysOnFocusedElement({
keys: [Key.Escape],
callback: handleEscape,
focusId: instanceId,
scope: InputHotkeyScope.TextInput,
dependencies: [handleEscape],
options: {
preventDefault: false,
},
);
});
useScopedHotkeys(
[Key.Enter],
() => {
if (!isFocused) {
return;
}
onInputEnter?.();
if (isDefined(inputRef) && 'current' in inputRef) {
setIsFocused(false);
}
},
InputHotkeyScope.TextInput,
[inputRef, isFocused, onInputEnter],
{
useHotkeysOnFocusedElement({
keys: [Key.Enter],
callback: handleEnter,
focusId: instanceId,
scope: InputHotkeyScope.TextInput,
dependencies: [handleEnter],
options: {
preventDefault: false,
},
);
});
return (
<TextInputV2
@@ -11,10 +11,10 @@ import {
useRef,
useState,
} from 'react';
import { IconComponent, IconEye, IconEyeOff } from 'twenty-ui/display';
import { AutogrowWrapper } from 'twenty-ui/utilities';
import { useCombinedRefs } from '~/hooks/useCombinedRefs';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
import { AutogrowWrapper } from 'twenty-ui/utilities';
import { IconComponent, IconEye, IconEyeOff } from 'twenty-ui/display';
const StyledContainer = styled.div<
Pick<TextInputV2ComponentProps, 'fullWidth'>
@@ -297,12 +297,12 @@ const TextInputV2Component = forwardRef<
onBlur?.(event);
};
const inputId = useId();
const instanceId = useId();
return (
<StyledContainer className={className} fullWidth={fullWidth ?? false}>
{label && (
<InputLabel htmlFor={inputId}>
<InputLabel htmlFor={instanceId}>
{label + (required ? '*' : '')}
</InputLabel>
)}
@@ -322,7 +322,7 @@ const TextInputV2Component = forwardRef<
)}
<StyledInput
id={inputId}
id={instanceId}
width={width}
data-testid={dataTestId}
autoComplete={autoComplete || 'off'}
@@ -6,11 +6,14 @@ import { useRef, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import styled from '@emotion/styled';
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
type InputProps = {
instanceId: string;
value?: string;
onChange: (value: string) => void;
placeholder?: string;
@@ -56,6 +59,7 @@ const StyledDiv = styled.div<{
`;
const Input = ({
instanceId,
value,
onChange,
placeholder,
@@ -78,14 +82,16 @@ const Input = ({
}
};
const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope();
const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById();
const handleLeaveFocus = () => {
setIsOpened(false);
goBackToPreviousHotkeyScope();
removeFocusItemFromFocusStackById({ focusId: instanceId });
};
useRegisterInputEvents<string>({
focusId: instanceId,
inputRef: wrapperRef,
inputValue: draftValue,
onEnter: () => {
@@ -131,6 +137,7 @@ const Input = ({
};
export const TitleInput = ({
instanceId,
disabled,
value,
sizeVariant = 'md',
@@ -145,12 +152,13 @@ export const TitleInput = ({
}: TitleInputProps) => {
const [isOpened, setIsOpened] = useState(false);
const { setHotkeyScopeAndMemorizePreviousScope } = usePreviousHotkeyScope();
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
return (
<>
{isOpened ? (
<Input
instanceId={instanceId}
sizeVariant={sizeVariant}
value={value}
onChange={onChange}
@@ -170,8 +178,16 @@ export const TitleInput = ({
onClick={() => {
if (!disabled) {
setIsOpened(true);
setHotkeyScopeAndMemorizePreviousScope({
scope: hotkeyScope,
pushFocusItemToFocusStack({
focusId: instanceId,
component: {
type: FocusComponentType.TEXT_INPUT,
instanceId: instanceId,
},
globalHotkeysConfig: {
enableGlobalHotkeysConflictingWithKeyboard: false,
},
hotkeyScope: { scope: hotkeyScope },
});
}
}}
@@ -1,49 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import {
AutosizeTextInput,
AutosizeTextInputVariant,
} from '../AutosizeTextInput';
import {
CatalogDecorator,
CatalogStory,
ComponentDecorator,
} from 'twenty-ui/testing';
const meta: Meta<typeof AutosizeTextInput> = {
title: 'UI/Input/AutosizeTextInput/AutosizeTextInput',
component: AutosizeTextInput,
decorators: [ComponentDecorator],
};
export default meta;
type Story = StoryObj<typeof AutosizeTextInput>;
export const Default: Story = {};
export const ButtonVariant: Story = {
args: { variant: AutosizeTextInputVariant.Button },
};
export const Catalog: CatalogStory<Story, typeof AutosizeTextInput> = {
parameters: {
catalog: {
dimensions: [
{
name: 'variants',
values: Object.values(AutosizeTextInputVariant),
props: (variant: AutosizeTextInputVariant) => ({ variant }),
labels: (variant: AutosizeTextInputVariant) =>
`variant -> ${variant}`,
},
{
name: 'minRows',
values: [1, 4],
props: (minRows: number) => ({ minRows }),
labels: (minRows: number) => `minRows -> ${minRows}`,
},
],
},
},
decorators: [CatalogDecorator],
};
@@ -72,6 +72,7 @@ export const RelativeDatePickerHeader = (
fullWidth
/>
<TextInput
instanceId="relative-date-picker-amount"
width={50}
value={textInputValue}
onChange={(text) => {
@@ -1,3 +0,0 @@
export enum IconPickerHotkeyScope {
IconPicker = 'icon-picker',
}