@jobber/components-native 0.90.1-JOB-142149-547612b.8 → 0.91.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +2 -2
- package/dist/src/Form/Form.js +1 -1
- package/dist/src/Form/components/FormBody/FormBody.js +5 -5
- package/dist/src/FormatFile/components/MediaView/MediaView.js +22 -5
- package/dist/src/InputDate/InputDate.js +2 -2
- package/dist/src/InputFieldWrapper/InputFieldWrapper.js +14 -12
- package/dist/src/InputFieldWrapper/components/Prefix/Prefix.js +5 -2
- package/dist/src/InputFieldWrapper/components/Suffix/Suffix.js +5 -2
- package/dist/src/InputPressable/InputPressable.js +20 -8
- package/dist/src/InputPressable/InputPressable.style.js +3 -0
- package/dist/src/InputText/InputText.js +22 -11
- package/dist/src/InputText/InputText.style.js +4 -0
- package/dist/src/InputTime/InputTime.js +2 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/InputDate/InputDate.d.ts +2 -1
- package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +9 -2
- package/dist/types/src/InputFieldWrapper/components/Prefix/Prefix.d.ts +2 -3
- package/dist/types/src/InputFieldWrapper/components/Suffix/Suffix.d.ts +2 -3
- package/dist/types/src/InputPressable/InputPressable.d.ts +9 -1
- package/dist/types/src/InputPressable/InputPressable.style.d.ts +3 -0
- package/dist/types/src/InputSearch/InputSearch.d.ts +1 -1
- package/dist/types/src/InputText/InputText.d.ts +8 -0
- package/dist/types/src/InputText/InputText.style.d.ts +4 -0
- package/dist/types/src/InputTime/InputTime.d.ts +2 -1
- package/package.json +2 -2
- package/src/Form/Form.tsx +1 -0
- package/src/Form/components/FormBody/FormBody.tsx +6 -6
- package/src/FormatFile/components/MediaView/MediaView.test.tsx +283 -0
- package/src/FormatFile/components/MediaView/MediaView.tsx +27 -6
- package/src/InputDate/InputDate.tsx +5 -1
- package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +48 -1
- package/src/InputFieldWrapper/InputFieldWrapper.tsx +38 -28
- package/src/InputFieldWrapper/components/Prefix/Prefix.test.tsx +3 -5
- package/src/InputFieldWrapper/components/Prefix/Prefix.tsx +6 -4
- package/src/InputFieldWrapper/components/Suffix/Suffix.test.tsx +2 -4
- package/src/InputFieldWrapper/components/Suffix/Suffix.tsx +6 -4
- package/src/InputPressable/InputPressable.style.ts +4 -0
- package/src/InputPressable/InputPressable.test.tsx +75 -1
- package/src/InputPressable/InputPressable.tsx +33 -7
- package/src/InputSearch/InputSearch.tsx +1 -0
- package/src/InputText/InputText.style.ts +5 -0
- package/src/InputText/InputText.test.tsx +75 -0
- package/src/InputText/InputText.tsx +32 -12
- package/src/InputTime/InputTime.tsx +5 -1
|
@@ -43,7 +43,14 @@ export interface InputFieldWrapperProps {
|
|
|
43
43
|
*/
|
|
44
44
|
readonly assistiveText?: string;
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Controls how the placeholder text is displayed.
|
|
48
|
+
* - normal: the placeholder text will be displayed in the normal placeholder position
|
|
49
|
+
* - mini: the placeholder text will float above the input value
|
|
50
|
+
* - hidden: the placeholder text will not be displayed
|
|
51
|
+
* @default "normal"
|
|
52
|
+
*/
|
|
53
|
+
readonly placeholderMode?: "normal" | "mini" | "hidden";
|
|
47
54
|
|
|
48
55
|
readonly hasValue?: boolean;
|
|
49
56
|
|
|
@@ -119,7 +126,7 @@ export function InputFieldWrapper({
|
|
|
119
126
|
assistiveText,
|
|
120
127
|
prefix,
|
|
121
128
|
suffix,
|
|
122
|
-
|
|
129
|
+
placeholderMode = "normal",
|
|
123
130
|
hasValue = false,
|
|
124
131
|
error,
|
|
125
132
|
focused = false,
|
|
@@ -143,6 +150,9 @@ export function InputFieldWrapper({
|
|
|
143
150
|
const showLoadingGlimmer = loading && loadingType === "glimmer";
|
|
144
151
|
const styles = useStyles();
|
|
145
152
|
|
|
153
|
+
const placeholderVisible = placeholderMode !== "hidden";
|
|
154
|
+
const miniLabelActive = placeholderMode === "mini";
|
|
155
|
+
|
|
146
156
|
return (
|
|
147
157
|
<ErrorMessageWrapper message={getMessage({ invalid, error })}>
|
|
148
158
|
<View
|
|
@@ -160,35 +170,36 @@ export function InputFieldWrapper({
|
|
|
160
170
|
<PrefixIcon
|
|
161
171
|
disabled={disabled}
|
|
162
172
|
focused={focused}
|
|
163
|
-
hasMiniLabel={hasMiniLabel}
|
|
164
173
|
inputInvalid={inputInvalid}
|
|
165
174
|
icon={prefix.icon}
|
|
166
175
|
/>
|
|
167
176
|
)}
|
|
168
177
|
<View style={[styles.inputContainer]}>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
178
|
+
{placeholderVisible && (
|
|
179
|
+
<View
|
|
180
|
+
style={[
|
|
181
|
+
!!placeholder && styles.label,
|
|
182
|
+
miniLabelActive && styles.miniLabel,
|
|
183
|
+
disabled && styles.disabled,
|
|
184
|
+
miniLabelActive &&
|
|
185
|
+
showClearAction &&
|
|
186
|
+
styles.miniLabelShowClearAction,
|
|
187
|
+
]}
|
|
188
|
+
pointerEvents="none"
|
|
189
|
+
>
|
|
190
|
+
<Placeholder
|
|
191
|
+
placeholder={placeholder}
|
|
192
|
+
labelVariation={getLabelVariation(error, invalid, disabled)}
|
|
193
|
+
miniLabelActive={miniLabelActive}
|
|
194
|
+
styleOverride={styleOverride?.placeholderText}
|
|
195
|
+
/>
|
|
196
|
+
</View>
|
|
197
|
+
)}
|
|
187
198
|
{prefix?.label && hasValue && (
|
|
188
199
|
<PrefixLabel
|
|
189
200
|
disabled={disabled}
|
|
190
201
|
focused={focused}
|
|
191
|
-
|
|
202
|
+
miniLabelActive={miniLabelActive}
|
|
192
203
|
inputInvalid={inputInvalid}
|
|
193
204
|
label={prefix.label}
|
|
194
205
|
styleOverride={styleOverride?.prefixLabel}
|
|
@@ -225,7 +236,7 @@ export function InputFieldWrapper({
|
|
|
225
236
|
<SuffixLabel
|
|
226
237
|
disabled={disabled}
|
|
227
238
|
focused={focused}
|
|
228
|
-
|
|
239
|
+
miniLabelActive={miniLabelActive}
|
|
229
240
|
inputInvalid={inputInvalid}
|
|
230
241
|
label={suffix.label}
|
|
231
242
|
hasLeftMargin={!showClearAction}
|
|
@@ -245,7 +256,6 @@ export function InputFieldWrapper({
|
|
|
245
256
|
<SuffixIcon
|
|
246
257
|
disabled={disabled}
|
|
247
258
|
focused={focused}
|
|
248
|
-
hasMiniLabel={hasMiniLabel}
|
|
249
259
|
hasLeftMargin={!!(!showClearAction || suffix?.label)}
|
|
250
260
|
inputInvalid={inputInvalid}
|
|
251
261
|
icon={suffix.icon}
|
|
@@ -332,12 +342,12 @@ function Placeholder({
|
|
|
332
342
|
placeholder,
|
|
333
343
|
styleOverride,
|
|
334
344
|
labelVariation,
|
|
335
|
-
|
|
345
|
+
miniLabelActive,
|
|
336
346
|
}: {
|
|
337
347
|
readonly placeholder?: string;
|
|
338
348
|
readonly styleOverride: StyleProp<TextStyle>;
|
|
339
349
|
readonly labelVariation: TextVariation;
|
|
340
|
-
readonly
|
|
350
|
+
readonly miniLabelActive: boolean;
|
|
341
351
|
}) {
|
|
342
352
|
const typographyStyles = useTypographyStyles();
|
|
343
353
|
|
|
@@ -348,7 +358,7 @@ function Placeholder({
|
|
|
348
358
|
hideFromScreenReader={true}
|
|
349
359
|
maxLines="single"
|
|
350
360
|
variation={labelVariation}
|
|
351
|
-
level={
|
|
361
|
+
level={miniLabelActive ? "textSupporting" : "text"}
|
|
352
362
|
>
|
|
353
363
|
{placeholder}
|
|
354
364
|
</Text>
|
|
@@ -361,7 +371,7 @@ function Placeholder({
|
|
|
361
371
|
style={[
|
|
362
372
|
typographyStyles[labelVariation],
|
|
363
373
|
typographyStyles.baseRegularRegular,
|
|
364
|
-
|
|
374
|
+
miniLabelActive
|
|
365
375
|
? typographyStyles.smallSize
|
|
366
376
|
: typographyStyles.defaultSize,
|
|
367
377
|
styleOverride,
|
|
@@ -30,7 +30,7 @@ beforeAll(() => {
|
|
|
30
30
|
function setupLabel({
|
|
31
31
|
disabled = false,
|
|
32
32
|
focused = false,
|
|
33
|
-
|
|
33
|
+
miniLabelActive = false,
|
|
34
34
|
inputInvalid = false,
|
|
35
35
|
label = mockLabel,
|
|
36
36
|
styleOverride,
|
|
@@ -39,7 +39,7 @@ function setupLabel({
|
|
|
39
39
|
<PrefixLabel
|
|
40
40
|
disabled={disabled}
|
|
41
41
|
focused={focused}
|
|
42
|
-
|
|
42
|
+
miniLabelActive={miniLabelActive}
|
|
43
43
|
inputInvalid={inputInvalid}
|
|
44
44
|
label={label}
|
|
45
45
|
styleOverride={styleOverride}
|
|
@@ -50,7 +50,6 @@ function setupLabel({
|
|
|
50
50
|
function setupIcon({
|
|
51
51
|
disabled = false,
|
|
52
52
|
focused = false,
|
|
53
|
-
hasMiniLabel = false,
|
|
54
53
|
inputInvalid = false,
|
|
55
54
|
icon = "invoice",
|
|
56
55
|
}: Partial<PrefixIconProps>) {
|
|
@@ -58,7 +57,6 @@ function setupIcon({
|
|
|
58
57
|
<PrefixIcon
|
|
59
58
|
disabled={disabled}
|
|
60
59
|
focused={focused}
|
|
61
|
-
hasMiniLabel={hasMiniLabel}
|
|
62
60
|
inputInvalid={inputInvalid}
|
|
63
61
|
icon={icon}
|
|
64
62
|
/>,
|
|
@@ -150,7 +148,7 @@ describe("Prefix", () => {
|
|
|
150
148
|
|
|
151
149
|
it("updates the position of the label when a value is entered", () => {
|
|
152
150
|
const tree = setupLabel({
|
|
153
|
-
|
|
151
|
+
miniLabelActive: true,
|
|
154
152
|
});
|
|
155
153
|
const prefixLabel = tree.getByTestId(prefixLabelTestId);
|
|
156
154
|
const labelWrapper = prefixLabel.children[0] as ReactTestInstance;
|
|
@@ -11,7 +11,7 @@ import { useStyles } from "../../InputFieldWrapper.style";
|
|
|
11
11
|
export interface PrefixLabelProps {
|
|
12
12
|
readonly focused: boolean;
|
|
13
13
|
readonly disabled?: boolean;
|
|
14
|
-
readonly
|
|
14
|
+
readonly miniLabelActive: boolean;
|
|
15
15
|
readonly inputInvalid: boolean;
|
|
16
16
|
readonly label: string;
|
|
17
17
|
readonly styleOverride?: StyleProp<TextStyle>;
|
|
@@ -23,7 +23,7 @@ export const prefixIconTestId = "ATL-InputFieldWrapper-PrefixIcon";
|
|
|
23
23
|
export function PrefixLabel({
|
|
24
24
|
focused,
|
|
25
25
|
disabled,
|
|
26
|
-
|
|
26
|
+
miniLabelActive,
|
|
27
27
|
inputInvalid,
|
|
28
28
|
label,
|
|
29
29
|
styleOverride,
|
|
@@ -41,7 +41,10 @@ export function PrefixLabel({
|
|
|
41
41
|
testID={prefixLabelTestId}
|
|
42
42
|
>
|
|
43
43
|
<View
|
|
44
|
-
style={[
|
|
44
|
+
style={[
|
|
45
|
+
styles.prefixLabel,
|
|
46
|
+
miniLabelActive && styles.fieldAffixMiniLabel,
|
|
47
|
+
]}
|
|
45
48
|
>
|
|
46
49
|
{!styleOverride ? (
|
|
47
50
|
<Text variation={disabled ? "disabled" : "base"}>{label}</Text>
|
|
@@ -67,7 +70,6 @@ export function PrefixLabel({
|
|
|
67
70
|
export interface PrefixIconProps {
|
|
68
71
|
readonly focused: boolean;
|
|
69
72
|
readonly disabled?: boolean;
|
|
70
|
-
readonly hasMiniLabel: boolean;
|
|
71
73
|
readonly inputInvalid?: boolean;
|
|
72
74
|
readonly icon: IconNames;
|
|
73
75
|
readonly styleOverride?: StyleProp<ViewStyle>;
|
|
@@ -10,7 +10,7 @@ const mockLabel = "$";
|
|
|
10
10
|
function setupLabel({
|
|
11
11
|
disabled = false,
|
|
12
12
|
focused = false,
|
|
13
|
-
|
|
13
|
+
miniLabelActive = false,
|
|
14
14
|
inputInvalid = false,
|
|
15
15
|
label = mockLabel,
|
|
16
16
|
styleOverride,
|
|
@@ -19,7 +19,7 @@ function setupLabel({
|
|
|
19
19
|
<SuffixLabel
|
|
20
20
|
disabled={disabled}
|
|
21
21
|
focused={focused}
|
|
22
|
-
|
|
22
|
+
miniLabelActive={miniLabelActive}
|
|
23
23
|
inputInvalid={inputInvalid}
|
|
24
24
|
label={label}
|
|
25
25
|
styleOverride={styleOverride}
|
|
@@ -30,7 +30,6 @@ function setupLabel({
|
|
|
30
30
|
function setupIcon({
|
|
31
31
|
disabled = false,
|
|
32
32
|
focused = false,
|
|
33
|
-
hasMiniLabel = false,
|
|
34
33
|
inputInvalid = false,
|
|
35
34
|
icon = "invoice",
|
|
36
35
|
}: Partial<SuffixIconProps>) {
|
|
@@ -38,7 +37,6 @@ function setupIcon({
|
|
|
38
37
|
<SuffixIcon
|
|
39
38
|
disabled={disabled}
|
|
40
39
|
focused={focused}
|
|
41
|
-
hasMiniLabel={hasMiniLabel}
|
|
42
40
|
inputInvalid={inputInvalid}
|
|
43
41
|
icon={icon}
|
|
44
42
|
/>,
|
|
@@ -11,7 +11,7 @@ import { useStyles } from "../../InputFieldWrapper.style";
|
|
|
11
11
|
export interface SuffixLabelProps {
|
|
12
12
|
readonly focused: boolean;
|
|
13
13
|
readonly disabled?: boolean;
|
|
14
|
-
readonly
|
|
14
|
+
readonly miniLabelActive: boolean;
|
|
15
15
|
readonly inputInvalid?: boolean;
|
|
16
16
|
readonly label: string;
|
|
17
17
|
readonly hasLeftMargin?: boolean;
|
|
@@ -24,7 +24,7 @@ export const suffixIconTestId = "ATL-InputFieldWrapper-SuffixIcon";
|
|
|
24
24
|
export function SuffixLabel({
|
|
25
25
|
focused,
|
|
26
26
|
disabled,
|
|
27
|
-
|
|
27
|
+
miniLabelActive,
|
|
28
28
|
inputInvalid,
|
|
29
29
|
label,
|
|
30
30
|
hasLeftMargin = true,
|
|
@@ -44,7 +44,10 @@ export function SuffixLabel({
|
|
|
44
44
|
]}
|
|
45
45
|
>
|
|
46
46
|
<View
|
|
47
|
-
style={[
|
|
47
|
+
style={[
|
|
48
|
+
styles.suffixLabel,
|
|
49
|
+
miniLabelActive && styles.fieldAffixMiniLabel,
|
|
50
|
+
]}
|
|
48
51
|
>
|
|
49
52
|
{!styleOverride ? (
|
|
50
53
|
<Text variation={disabled ? "disabled" : "base"}>{label}</Text>
|
|
@@ -70,7 +73,6 @@ export function SuffixLabel({
|
|
|
70
73
|
export interface SuffixIconProps {
|
|
71
74
|
readonly focused: boolean;
|
|
72
75
|
readonly disabled?: boolean;
|
|
73
|
-
readonly hasMiniLabel: boolean;
|
|
74
76
|
readonly inputInvalid?: boolean;
|
|
75
77
|
readonly icon: IconNames;
|
|
76
78
|
readonly hasLeftMargin?: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { fireEvent, render } from "@testing-library/react-native";
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react-native";
|
|
3
3
|
import { InputPressable } from ".";
|
|
4
4
|
import type { InputFieldWrapperProps } from "../InputFieldWrapper";
|
|
5
5
|
|
|
@@ -120,6 +120,80 @@ describe("InputPressable", () => {
|
|
|
120
120
|
expect(getByText(value, { includeHiddenElements: true })).toBeDefined();
|
|
121
121
|
});
|
|
122
122
|
});
|
|
123
|
+
|
|
124
|
+
describe("showMiniLabel", () => {
|
|
125
|
+
it("defaults to true", () => {
|
|
126
|
+
const props = { placeholder: "placeholder", value: "value" };
|
|
127
|
+
render(<InputPressable {...props} />);
|
|
128
|
+
expect(
|
|
129
|
+
screen.getByText("placeholder", { includeHiddenElements: true }),
|
|
130
|
+
).toBeDefined();
|
|
131
|
+
expect(MockInputFieldWrapper).toHaveBeenCalledWith(
|
|
132
|
+
expect.objectContaining({
|
|
133
|
+
placeholderMode: "mini",
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("when true", () => {
|
|
139
|
+
it("renders the placeholder in its normal position when the input has no value", () => {
|
|
140
|
+
const props = { showMiniLabel: true, placeholder: "placeholder" };
|
|
141
|
+
render(<InputPressable {...props} />);
|
|
142
|
+
expect(
|
|
143
|
+
screen.getByText("placeholder", { includeHiddenElements: true }),
|
|
144
|
+
).toBeDefined();
|
|
145
|
+
expect(MockInputFieldWrapper).toHaveBeenCalledWith(
|
|
146
|
+
expect.objectContaining({
|
|
147
|
+
placeholderMode: "normal",
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("renders the placeholder as a mini label when the input has a value", () => {
|
|
153
|
+
const props = {
|
|
154
|
+
showMiniLabel: true,
|
|
155
|
+
placeholder: "placeholder",
|
|
156
|
+
value: "value",
|
|
157
|
+
};
|
|
158
|
+
render(<InputPressable {...props} />);
|
|
159
|
+
expect(
|
|
160
|
+
screen.getByText("placeholder", { includeHiddenElements: true }),
|
|
161
|
+
).toBeDefined();
|
|
162
|
+
expect(MockInputFieldWrapper).toHaveBeenCalledWith(
|
|
163
|
+
expect.objectContaining({
|
|
164
|
+
placeholderMode: "mini",
|
|
165
|
+
}),
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe("when false", () => {
|
|
171
|
+
it("renders the placeholder in its normal position when the input has no value", () => {
|
|
172
|
+
const props = { showMiniLabel: false, placeholder: "placeholder" };
|
|
173
|
+
render(<InputPressable {...props} />);
|
|
174
|
+
expect(
|
|
175
|
+
screen.getByText("placeholder", { includeHiddenElements: true }),
|
|
176
|
+
).toBeDefined();
|
|
177
|
+
expect(MockInputFieldWrapper).toHaveBeenCalledWith(
|
|
178
|
+
expect.objectContaining({
|
|
179
|
+
placeholderMode: "normal",
|
|
180
|
+
}),
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("does not render the placeholder when the input has a value", () => {
|
|
185
|
+
const props = {
|
|
186
|
+
showMiniLabel: false,
|
|
187
|
+
placeholder: "placeholder",
|
|
188
|
+
value: "value",
|
|
189
|
+
};
|
|
190
|
+
render(<InputPressable {...props} />);
|
|
191
|
+
expect(
|
|
192
|
+
screen.queryByText("placeholder", { includeHiddenElements: true }),
|
|
193
|
+
).toBeNull();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|
|
123
197
|
});
|
|
124
198
|
|
|
125
199
|
describe("accessibilityLabel", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Ref } from "react";
|
|
2
|
-
import React, { forwardRef
|
|
2
|
+
import React, { forwardRef } from "react";
|
|
3
3
|
import type { IconNames } from "@jobber/design";
|
|
4
4
|
import type { FieldError } from "react-hook-form";
|
|
5
5
|
import { Text as NativeText, Pressable } from "react-native";
|
|
@@ -7,6 +7,7 @@ import type { Clearable } from "@jobber/hooks";
|
|
|
7
7
|
import { useShowClear } from "@jobber/hooks";
|
|
8
8
|
import type { XOR } from "ts-xor";
|
|
9
9
|
import { useStyles } from "./InputPressable.style";
|
|
10
|
+
import type { InputFieldWrapperProps } from "../InputFieldWrapper";
|
|
10
11
|
import { InputFieldWrapper, useCommonInputStyles } from "../InputFieldWrapper";
|
|
11
12
|
|
|
12
13
|
interface BasicSuffix {
|
|
@@ -51,6 +52,15 @@ export interface InputPressableProps {
|
|
|
51
52
|
*/
|
|
52
53
|
readonly invalid?: boolean | string;
|
|
53
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Controls the visibility of the mini label that appears inside the input
|
|
57
|
+
* when a value is entered. By default, the placeholder text moves up to
|
|
58
|
+
* become a mini label. Set to false to disable this behavior.
|
|
59
|
+
*
|
|
60
|
+
* @default true
|
|
61
|
+
*/
|
|
62
|
+
readonly showMiniLabel?: boolean;
|
|
63
|
+
|
|
54
64
|
/**
|
|
55
65
|
* Callback that is called when the text input is focused
|
|
56
66
|
* @param event
|
|
@@ -105,6 +115,7 @@ export function InputPressableInternal(
|
|
|
105
115
|
disabled,
|
|
106
116
|
invalid,
|
|
107
117
|
error,
|
|
118
|
+
showMiniLabel = true,
|
|
108
119
|
onPress,
|
|
109
120
|
accessibilityLabel,
|
|
110
121
|
accessibilityHint,
|
|
@@ -117,11 +128,8 @@ export function InputPressableInternal(
|
|
|
117
128
|
ref: Ref<InputPressableRef>,
|
|
118
129
|
): JSX.Element {
|
|
119
130
|
const hasValue = !!value;
|
|
120
|
-
const [hasMiniLabel, setHasMiniLabel] = useState(Boolean(value));
|
|
121
131
|
|
|
122
|
-
|
|
123
|
-
setHasMiniLabel(Boolean(value));
|
|
124
|
-
}, [value]);
|
|
132
|
+
const placeholderMode = getPlaceholderMode(showMiniLabel, value);
|
|
125
133
|
|
|
126
134
|
const showClear = useShowClear({
|
|
127
135
|
clearable,
|
|
@@ -139,7 +147,7 @@ export function InputPressableInternal(
|
|
|
139
147
|
prefix={prefix}
|
|
140
148
|
suffix={suffix}
|
|
141
149
|
hasValue={hasValue}
|
|
142
|
-
|
|
150
|
+
placeholderMode={placeholderMode}
|
|
143
151
|
focused={focused}
|
|
144
152
|
error={error}
|
|
145
153
|
invalid={invalid}
|
|
@@ -160,7 +168,8 @@ export function InputPressableInternal(
|
|
|
160
168
|
style={[
|
|
161
169
|
commonInputStyles.input,
|
|
162
170
|
styles.inputPressableStyles,
|
|
163
|
-
|
|
171
|
+
placeholderMode === "normal" && commonInputStyles.inputEmpty,
|
|
172
|
+
placeholderMode === "hidden" && styles.withoutMiniLabel,
|
|
164
173
|
disabled && commonInputStyles.inputDisabled,
|
|
165
174
|
(Boolean(invalid) || error) && styles.inputInvalid,
|
|
166
175
|
]}
|
|
@@ -176,3 +185,20 @@ export function InputPressableInternal(
|
|
|
176
185
|
</InputFieldWrapper>
|
|
177
186
|
);
|
|
178
187
|
}
|
|
188
|
+
|
|
189
|
+
function getPlaceholderMode(
|
|
190
|
+
isMiniLabelAllowed: boolean,
|
|
191
|
+
internalValue: string | undefined,
|
|
192
|
+
): InputFieldWrapperProps["placeholderMode"] {
|
|
193
|
+
const hasValue = Boolean(internalValue);
|
|
194
|
+
|
|
195
|
+
if (hasValue) {
|
|
196
|
+
if (isMiniLabelAllowed) {
|
|
197
|
+
return "mini";
|
|
198
|
+
} else {
|
|
199
|
+
return "hidden";
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
return "normal";
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -27,5 +27,10 @@ export const useStyles = buildThemedStyles(tokens => {
|
|
|
27
27
|
paddingTop:
|
|
28
28
|
(typographyStyles.defaultSize.fontSize || 0) + tokens["space-smallest"],
|
|
29
29
|
},
|
|
30
|
+
|
|
31
|
+
multilineWithoutMiniLabel: {
|
|
32
|
+
paddingTop: tokens["space-base"] + tokens["space-smallest"],
|
|
33
|
+
paddingBottom: tokens["space-base"] + tokens["space-smallest"],
|
|
34
|
+
},
|
|
30
35
|
};
|
|
31
36
|
});
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
fireEvent,
|
|
5
5
|
render,
|
|
6
6
|
renderHook,
|
|
7
|
+
screen,
|
|
7
8
|
waitFor,
|
|
8
9
|
} from "@testing-library/react-native";
|
|
9
10
|
import type { TextStyle } from "react-native";
|
|
@@ -548,6 +549,80 @@ describe("InputText", () => {
|
|
|
548
549
|
expect(styleOverride.inputText.color).toEqual(flattenedStyle.color);
|
|
549
550
|
});
|
|
550
551
|
});
|
|
552
|
+
|
|
553
|
+
describe("showMiniLabel", () => {
|
|
554
|
+
it("defaults to true", () => {
|
|
555
|
+
const props = { placeholder: "placeholder", value: "value" };
|
|
556
|
+
renderInputText(props);
|
|
557
|
+
expect(
|
|
558
|
+
screen.getByText("placeholder", { includeHiddenElements: true }),
|
|
559
|
+
).toBeDefined();
|
|
560
|
+
expect(MockInputFieldWrapper).toHaveBeenCalledWith(
|
|
561
|
+
expect.objectContaining({
|
|
562
|
+
placeholderMode: "mini",
|
|
563
|
+
}),
|
|
564
|
+
);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
describe("when true", () => {
|
|
568
|
+
it("renders the placeholder in its normal position when the input has no value", () => {
|
|
569
|
+
const props = { showMiniLabel: true, placeholder: "placeholder" };
|
|
570
|
+
renderInputText(props);
|
|
571
|
+
expect(
|
|
572
|
+
screen.getByText("placeholder", { includeHiddenElements: true }),
|
|
573
|
+
).toBeDefined();
|
|
574
|
+
expect(MockInputFieldWrapper).toHaveBeenCalledWith(
|
|
575
|
+
expect.objectContaining({
|
|
576
|
+
placeholderMode: "normal",
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
it("renders the placeholder as a mini label when the input has a value", () => {
|
|
582
|
+
const props = {
|
|
583
|
+
showMiniLabel: true,
|
|
584
|
+
placeholder: "placeholder",
|
|
585
|
+
value: "value",
|
|
586
|
+
};
|
|
587
|
+
renderInputText(props);
|
|
588
|
+
expect(
|
|
589
|
+
screen.getByText("placeholder", { includeHiddenElements: true }),
|
|
590
|
+
).toBeDefined();
|
|
591
|
+
expect(MockInputFieldWrapper).toHaveBeenCalledWith(
|
|
592
|
+
expect.objectContaining({
|
|
593
|
+
placeholderMode: "mini",
|
|
594
|
+
}),
|
|
595
|
+
);
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
describe("when false", () => {
|
|
600
|
+
it("renders the placeholder in its normal position when the input has no value", () => {
|
|
601
|
+
const props = { showMiniLabel: false, placeholder: "placeholder" };
|
|
602
|
+
renderInputText(props);
|
|
603
|
+
expect(
|
|
604
|
+
screen.getByText("placeholder", { includeHiddenElements: true }),
|
|
605
|
+
).toBeDefined();
|
|
606
|
+
expect(MockInputFieldWrapper).toHaveBeenCalledWith(
|
|
607
|
+
expect.objectContaining({
|
|
608
|
+
placeholderMode: "normal",
|
|
609
|
+
}),
|
|
610
|
+
);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
it("does not render the placeholder when the input has a value", () => {
|
|
614
|
+
const props = {
|
|
615
|
+
showMiniLabel: false,
|
|
616
|
+
placeholder: "placeholder",
|
|
617
|
+
value: "value",
|
|
618
|
+
};
|
|
619
|
+
renderInputText(props);
|
|
620
|
+
expect(
|
|
621
|
+
screen.queryByText("placeholder", { includeHiddenElements: true }),
|
|
622
|
+
).toBeNull();
|
|
623
|
+
});
|
|
624
|
+
});
|
|
625
|
+
});
|
|
551
626
|
});
|
|
552
627
|
|
|
553
628
|
describe("Transform", () => {
|
|
@@ -65,6 +65,15 @@ export interface InputTextProps
|
|
|
65
65
|
*/
|
|
66
66
|
readonly assistiveText?: string;
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Controls the visibility of the mini label that appears inside the input
|
|
70
|
+
* when a value is entered. By default, the placeholder text moves up to
|
|
71
|
+
* become a mini label. Set to false to disable this behavior.
|
|
72
|
+
*
|
|
73
|
+
* @default true
|
|
74
|
+
*/
|
|
75
|
+
readonly showMiniLabel?: boolean;
|
|
76
|
+
|
|
68
77
|
/**
|
|
69
78
|
* Determines what keyboard is shown
|
|
70
79
|
*/
|
|
@@ -245,6 +254,7 @@ function InputTextInternal(
|
|
|
245
254
|
name,
|
|
246
255
|
placeholder,
|
|
247
256
|
assistiveText,
|
|
257
|
+
showMiniLabel = true,
|
|
248
258
|
keyboard,
|
|
249
259
|
value: controlledValue,
|
|
250
260
|
defaultValue,
|
|
@@ -292,7 +302,8 @@ function InputTextInternal(
|
|
|
292
302
|
|
|
293
303
|
const hasValue = internalValue !== "" && internalValue !== undefined;
|
|
294
304
|
const [focused, setFocused] = useState(false);
|
|
295
|
-
const
|
|
305
|
+
const placeholderMode = getPlaceholderMode(showMiniLabel, internalValue);
|
|
306
|
+
const miniLabelActive = placeholderMode === "mini";
|
|
296
307
|
|
|
297
308
|
const textInputRef = useTextInputRef({ ref, onClear: handleClear });
|
|
298
309
|
|
|
@@ -367,7 +378,7 @@ function InputTextInternal(
|
|
|
367
378
|
prefix={prefix}
|
|
368
379
|
suffix={suffix}
|
|
369
380
|
hasValue={hasValue}
|
|
370
|
-
|
|
381
|
+
placeholderMode={placeholderMode}
|
|
371
382
|
assistiveText={assistiveText}
|
|
372
383
|
focused={focused}
|
|
373
384
|
error={error}
|
|
@@ -391,11 +402,14 @@ function InputTextInternal(
|
|
|
391
402
|
style={[
|
|
392
403
|
commonInputStyles.input,
|
|
393
404
|
styles.inputPaddingTop,
|
|
394
|
-
!
|
|
405
|
+
!miniLabelActive && commonInputStyles.inputEmpty,
|
|
395
406
|
disabled && commonInputStyles.inputDisabled,
|
|
396
407
|
multiline && styles.multiLineInput,
|
|
397
408
|
multiline && Platform.OS === "ios" && styles.multilineInputiOS,
|
|
398
|
-
multiline &&
|
|
409
|
+
multiline && miniLabelActive && styles.multiLineInputWithMini,
|
|
410
|
+
multiline &&
|
|
411
|
+
placeholderMode === "hidden" &&
|
|
412
|
+
styles.multilineWithoutMiniLabel,
|
|
399
413
|
styleOverride?.inputText,
|
|
400
414
|
loading && loadingType === "glimmer" && { color: "transparent" },
|
|
401
415
|
]}
|
|
@@ -510,13 +524,19 @@ function useTextInputRef({ ref, onClear }: UseTextInputRefProps) {
|
|
|
510
524
|
return textInputRef;
|
|
511
525
|
}
|
|
512
526
|
|
|
513
|
-
function
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
setHasMiniLabel(Boolean(internalValue));
|
|
519
|
-
}, [internalValue]);
|
|
527
|
+
function getPlaceholderMode(
|
|
528
|
+
isMiniLabelAllowed: boolean,
|
|
529
|
+
internalValue: string,
|
|
530
|
+
): InputFieldWrapperProps["placeholderMode"] {
|
|
531
|
+
const hasValue = Boolean(internalValue);
|
|
520
532
|
|
|
521
|
-
|
|
533
|
+
if (hasValue) {
|
|
534
|
+
if (isMiniLabelAllowed) {
|
|
535
|
+
return "mini";
|
|
536
|
+
} else {
|
|
537
|
+
return "hidden";
|
|
538
|
+
}
|
|
539
|
+
} else {
|
|
540
|
+
return "normal";
|
|
541
|
+
}
|
|
522
542
|
}
|