Skip to content

Commit f8e084a

Browse files
authored
fix: placeholders & password autocomplete (#78)
* Fix placeholders & password autocomplete * Address feedback * Update tests * Lint * Add input props * Put autocomplete back * add input props
1 parent 4929254 commit f8e084a

4 files changed

Lines changed: 54 additions & 33 deletions

File tree

src/controls/PrimitiveArrayControl.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe("PrimitiveArrayControl", () => {
2020
schema: stringArrayControlJsonSchema,
2121
uischema: arrayControlUISchema,
2222
})
23-
await screen.findByPlaceholderText("Enter value")
23+
await screen.findByPlaceholderText("value")
2424
screen.getByRole("button", { name: "Add Assets" })
2525
})
2626

@@ -75,7 +75,7 @@ describe("PrimitiveArrayControl", () => {
7575
data = result.data as JSONFormData<typeof stringArrayControlJsonSchema>
7676
},
7777
})
78-
const newAsset = await screen.findByPlaceholderText("Enter value")
78+
const newAsset = await screen.findByPlaceholderText("value")
7979
await user.type(newAsset, "new")
8080
screen.getByDisplayValue("new")
8181
await waitFor(() => {
@@ -94,7 +94,7 @@ describe("PrimitiveArrayControl", () => {
9494
const addButton = await screen.findByRole("button", { name: "Add Assets" })
9595
await user.click(addButton)
9696
await user.click(addButton)
97-
const inputFields = await screen.findAllByPlaceholderText("Enter value")
97+
const inputFields = await screen.findAllByPlaceholderText("value")
9898
await user.type(inputFields[0], "my asset")
9999
await user.type(inputFields[1], "remove me!")
100100
await user.type(inputFields[2], "my other asset")

src/controls/TextControl.test.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ test("renders default value when present", async () => {
4242
await waitFor(() => {
4343
expect(
4444
screen.getByPlaceholderText(
45-
"Enter " + defaultValueTextInputSchema.properties.foo.title,
45+
defaultValueTextInputSchema.properties.foo.title,
4646
{ exact: false },
4747
),
4848
).toHaveValue(defaultValueTextInputSchema.properties.foo.default)
@@ -60,7 +60,7 @@ test("updates jsonforms data as expected", async () => {
6060
})
6161

6262
const input = screen.getByPlaceholderText(
63-
"Enter " + textInputSchema.properties.foo.title,
63+
textInputSchema.properties.foo.title,
6464
{ exact: false },
6565
)
6666

@@ -88,10 +88,9 @@ test("renders a password when present", async () => {
8888
} satisfies JSONSchema
8989

9090
render({ schema: passwordSchema, uischema: passwordUISchema })
91-
await screen.findByPlaceholderText(
92-
"Enter " + passwordSchema.properties.secret.title,
93-
{ exact: false },
94-
)
91+
await screen.findByPlaceholderText(passwordSchema.properties.secret.title, {
92+
exact: false,
93+
})
9594
expect(
9695
(screen.getByLabelText("Secret") satisfies HTMLInputElement).type,
9796
).toEqual("password")
@@ -122,7 +121,7 @@ test("renders error messages from rule validation", async () => {
122121
})
123122

124123
const inputElement = await screen.findByPlaceholderText(
125-
"Enter " + patternSchema.properties.name.title,
124+
patternSchema.properties.name.title,
126125
{ exact: false },
127126
)
128127

src/controls/TextControl.tsx

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import type { ChangeEvent } from "react"
22
import { useCallback, useEffect } from "react"
3-
import type { InputProps } from "antd"
43
import { Input, Form } from "antd"
54
import { QuestionCircleOutlined } from "@ant-design/icons"
65
import type { Rule } from "antd/es/form"
7-
import type { TextAreaProps } from "antd/es/input"
86
import type { ControlProps } from "@jsonforms/core"
97

10-
import type { TextControlOptions, TextControlType } from "../ui-schema"
8+
import type { TextControlOptions } from "../ui-schema"
119
import { assertNever } from "../common/assert-never"
1210
import { withJsonFormsControlProps } from "@jsonforms/react"
1311
interface TextControlProps extends ControlProps {
@@ -40,7 +38,6 @@ export function TextControl({
4038
const ariaLabel = label || schema.description
4139
const options: TextControlOptions =
4240
(uischema.options as TextControlOptions) ?? {}
43-
const textControlType: TextControlType = options.type ?? "singleline"
4441
const tooltip = options.tooltip
4542
const placeholderText = options.placeholderText
4643
const form = Form.useFormInstance()
@@ -73,41 +70,52 @@ export function TextControl({
7370
: {})}
7471
>
7572
<TextControlInput
76-
type={textControlType}
7773
aria-label={ariaLabel}
7874
disabled={!enabled}
7975
autoComplete="off"
8076
onChange={(e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) =>
8177
handleChange(path, e.target.value)
8278
}
83-
placeholder={`Enter ${
84-
placeholderText ?? (label.toLowerCase() || "value")
85-
}`}
79+
placeholder={placeholderText ?? (label.toLowerCase() || "value")}
80+
textControlOptions={options}
8681
/>
8782
</Form.Item>
8883
)
8984
}
9085

91-
type TextControlInputProps =
92-
| (InputProps & { type: "singleline" })
93-
| (TextAreaProps & { type: "multiline" })
94-
| (InputProps & { type: "password" })
86+
type TextControlInputProps = {
87+
"aria-label": string | undefined
88+
disabled: boolean
89+
autoComplete: string
90+
onChange: (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void
91+
placeholder: string
92+
textControlOptions: TextControlOptions
93+
}
94+
95+
function TextControlInput({
96+
textControlOptions,
97+
...rest
98+
}: TextControlInputProps) {
99+
if (
100+
!(`type` in textControlOptions) ||
101+
textControlOptions.type === undefined
102+
) {
103+
return <Input {...{ ...rest, ...textControlOptions }} />
104+
}
95105

96-
function TextControlInput({ type, ...rest }: TextControlInputProps) {
97-
switch (type) {
106+
switch (textControlOptions.type) {
98107
case "multiline":
99-
// idk why type isn't getting narrowed properly here, but cast seems safe
100-
return <Input.TextArea {...(rest as TextAreaProps)} />
108+
return <Input.TextArea {...{ ...rest, ...textControlOptions }} />
101109
case "singleline":
102-
// idk why type isn't getting narrowed properly here, but cast seems safe
103-
return <Input {...(rest as InputProps)} />
110+
return <Input {...{ ...rest, ...textControlOptions }} />
104111
case "password":
105-
return <Input.Password {...(rest as InputProps)} />
112+
return <Input.Password {...{ ...rest, ...textControlOptions }} />
113+
106114
default:
107115
try {
108-
assertNever(type)
116+
assertNever(textControlOptions.type)
109117
} catch (e) {
110-
return <Input {...(rest as InputProps)} />
118+
return <Input {...{ ...rest, ...textControlOptions }} />
111119
}
112120
}
113121
}

src/ui-schema.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import type {
55
AlertProps,
66
CardProps,
77
DividerProps,
8+
InputProps,
89
} from "antd"
10+
import type { TextAreaProps } from "antd/es/input"
911
import type { RuleObject as AntDRule } from "antd/es/form"
1012
import type { TitleProps } from "antd/es/typography/Title"
1113
import type { TextProps } from "antd/es/typography/Text"
@@ -152,12 +154,24 @@ export type AnyOfControlOptions = OneOfControlOptions
152154
export type TextControlType = "multiline" | "password" | "singleline"
153155

154156
export type TextControlOptions = {
155-
type?: TextControlType
156157
tooltip?: string
157158
placeholderText?: string
158159
required?: boolean
159160
rules?: AntDRule[]
160-
}
161+
} & (
162+
| {
163+
type?: "singleline"
164+
inputProps?: InputProps
165+
}
166+
| {
167+
type: "multiline"
168+
inputProps?: TextAreaProps
169+
}
170+
| {
171+
type: "password"
172+
inputProps?: InputProps
173+
}
174+
)
161175

162176
/**
163177
* A control element. The scope property of the control determines

0 commit comments

Comments
 (0)