Skip to content

Commit 95601dc

Browse files
committed
fix(TextInputMapper): add keyboard interaction
1 parent f3d18c8 commit 95601dc

File tree

6 files changed

+53
-17
lines changed

6 files changed

+53
-17
lines changed

src/components/fields/TextInputMapper/TextInputMapper.tsx

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
22
ComponentType,
3+
ForwardedRef,
34
forwardRef,
5+
KeyboardEvent,
46
useEffect,
57
useMemo,
68
useRef,
@@ -9,7 +11,7 @@ import {
911

1012
import { useEvent } from '../../../_internal/hooks';
1113
import { FieldBaseProps } from '../../../shared';
12-
import { mergeProps } from '../../../utils/react/index';
14+
import { mergeProps, useCombinedRefs } from '../../../utils/react/index';
1315
import { useFieldProps, useFormProps, wrapWithField } from '../../form';
1416
import { CloseIcon, PlusIcon } from '../../../icons';
1517
import { Button } from '../../actions';
@@ -63,7 +65,12 @@ function removeDuplicates(mappings: Mapping[]) {
6365
});
6466
}
6567

66-
function TextInputMapper(props: CubeTextInputMapperProps, ref: any) {
68+
function TextInputMapper(
69+
props: CubeTextInputMapperProps,
70+
ref: ForwardedRef<HTMLDivElement>,
71+
) {
72+
ref = useCombinedRefs(ref);
73+
6774
props = useFormProps(props);
6875
props = useFieldProps(props, {
6976
defaultValidationTrigger: 'onChange',
@@ -122,7 +129,7 @@ function TextInputMapper(props: CubeTextInputMapperProps, ref: any) {
122129
const onMappingsChange = useEvent((newMappings: Mapping[]) => {
123130
const newValue = newMappings.reduce(
124131
(acc, { key, value }) => {
125-
acc[key] = value;
132+
acc[key.trim()] = value.trim();
126133

127134
return acc;
128135
},
@@ -136,8 +143,25 @@ function TextInputMapper(props: CubeTextInputMapperProps, ref: any) {
136143
} else {
137144
onChange?.(newValue);
138145
}
146+
147+
const updatedMappings = extractLocalValues(newValue ?? {}, newMappings);
148+
149+
if (JSON.stringify(updatedMappings) !== JSON.stringify(mappings)) {
150+
setMappings(updatedMappings);
151+
}
139152
});
140153

154+
// useEffect(() => {
155+
// // focus on the last non-disabled input
156+
// setTimeout(() => {
157+
// (
158+
// ref?.current?.querySelector(
159+
// '[data-qa="Mapping"]:last-child input:not([disabled])',
160+
// ) as HTMLInputElement
161+
// )?.focus();
162+
// }, 100);
163+
// }, [mappings.length]);
164+
141165
const addNewMapping = useEvent(() => {
142166
setMappings((prev) => {
143167
return [...prev, { key: '', value: '', id: counterRef.current++ }];
@@ -176,17 +200,26 @@ function TextInputMapper(props: CubeTextInputMapperProps, ref: any) {
176200
onMappingsChange(mappings);
177201
});
178202

203+
const onKeyDown = useEvent((e: KeyboardEvent<HTMLDivElement>) => {
204+
// if Ctrl+Enter or Cmd+Enter is pressed then add new mapping if that's enabled
205+
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter' && showNewButton) {
206+
addNewMapping();
207+
}
208+
});
209+
179210
const renderedMappings = useMemo(() => {
180-
return mappings.map((mapping) => {
211+
return mappings.map((mapping, index) => {
181212
const { key, value, id } = mapping;
182213

183214
return (
184215
<Grid
185216
key={id}
217+
qa="Mapping"
186218
columns="minmax(0, 1fr) minmax(0, 1fr) min-content"
187219
gap="1x"
188220
>
189221
<TextInputMapperInput
222+
autoFocus={index === mappings.length - 1}
190223
id={id}
191224
isDisabled={isDisabled}
192225
type="name"
@@ -222,8 +255,10 @@ function TextInputMapper(props: CubeTextInputMapperProps, ref: any) {
222255
}, [JSON.stringify(mappings)]);
223256

224257
const element = (
225-
<Flow gap="1x">
226-
{[...renderedMappings]}
258+
<Flow ref={ref} gap="1x">
259+
<Flow gap="1x" onKeyDown={onKeyDown}>
260+
{[...renderedMappings]}
261+
</Flow>
227262
{showNewButton ? (
228263
<Space gap={0}>
229264
{/** Hotfix for inconsistent alignment with the label **/}
@@ -251,6 +286,7 @@ export interface CubeTextInputMapperInputProps {
251286
onChange?: (id: number, newValue: string) => void;
252287
onSubmit?: (id: number) => void;
253288
isDisabled?: boolean;
289+
autoFocus?: boolean;
254290
}
255291

256292
function TextInputMapperInput(props: CubeTextInputMapperInputProps) {

src/components/layout/Flex.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { forwardRef } from 'react';
22

33
import {
4-
BaseProps,
4+
AllBaseProps,
55
CONTAINER_STYLES,
66
ContainerStyleProps,
77
extractStyles,
@@ -16,7 +16,7 @@ const FlexElement = tasty({
1616
},
1717
});
1818

19-
export interface CubeFlexProps extends BaseProps, ContainerStyleProps {}
19+
export interface CubeFlexProps extends AllBaseProps, ContainerStyleProps {}
2020

2121
export const Flex = forwardRef(function Flex(props: CubeFlexProps, ref) {
2222
const styles = extractStyles(props, CONTAINER_STYLES);

src/components/layout/Flow.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { forwardRef } from 'react';
22

33
import {
4-
BaseProps,
4+
AllBaseProps,
55
CONTAINER_STYLES,
66
ContainerStyleProps,
77
extractStyles,
@@ -18,7 +18,7 @@ const FlowElement = tasty({
1818

1919
const STYLE_PROPS = CONTAINER_STYLES;
2020

21-
export interface CubeFlowProps extends BaseProps, ContainerStyleProps {}
21+
export interface CubeFlowProps extends AllBaseProps, ContainerStyleProps {}
2222

2323
export const Flow = forwardRef(function Flow(props: CubeFlowProps, ref) {
2424
const styles = extractStyles(props, STYLE_PROPS);

src/components/layout/Grid.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { forwardRef } from 'react';
22

33
import {
4-
BaseProps,
4+
AllBaseProps,
55
CONTAINER_STYLES,
66
ContainerStyleProps,
77
extractStyles,
@@ -18,7 +18,7 @@ const GridElement = tasty({
1818
});
1919

2020
export interface CubeGridProps
21-
extends BaseProps,
21+
extends Omit<AllBaseProps, 'rows'>,
2222
ContainerStyleProps,
2323
ShortGridStyles {}
2424

src/components/layout/Space.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { forwardRef } from 'react';
22

33
import {
4-
BaseProps,
4+
AllBaseProps,
55
CONTAINER_STYLES,
66
ContainerStyleProps,
77
extractStyles,
@@ -24,7 +24,7 @@ const SpaceElement = tasty({
2424
},
2525
});
2626

27-
export interface CubeSpaceProps extends BaseProps, ContainerStyleProps {
27+
export interface CubeSpaceProps extends AllBaseProps, ContainerStyleProps {
2828
direction?: 'vertical' | 'horizontal';
2929
}
3030

src/tasty/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ export interface BasePropsWithoutChildren
8080
theme?: 'default' | 'danger' | 'special' | (string & {});
8181
}
8282

83-
export interface BaseProps
83+
export interface BaseProps<K extends keyof HTMLElementTagNameMap = 'div'>
8484
extends AriaLabelingProps,
8585
BasePropsWithoutChildren,
86-
Pick<AllHTMLAttributes<HTMLElementTagNameMap['div']>, 'children'> {}
86+
Pick<AllHTMLAttributes<HTMLElementTagNameMap[K]>, 'children'> {}
8787

8888
export interface AllBaseProps<K extends keyof HTMLElementTagNameMap = 'div'>
8989
extends BaseProps,
@@ -98,7 +98,7 @@ export interface AllBaseProps<K extends keyof HTMLElementTagNameMap = 'div'>
9898
| 'height'
9999
| 'width'
100100
> {
101-
as?: string;
101+
as?: K;
102102
}
103103

104104
export type BaseStyleProps = Pick<Styles, (typeof BASE_STYLES)[number]>;

0 commit comments

Comments
 (0)