Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ObjectArrayControl #33

Merged
merged 21 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@
"@ant-design/icons": "^5.3.0",
"@jsonforms/core": "^3.2.1",
"@jsonforms/react": "^3.2.1",
"@types/lodash.isempty": "^4.4.9",
"antd": "^5.14.0",
"lodash.isempty": "^4.4.0",
"react": "^17 || ^18"
},
"devDependencies": {
Expand All @@ -72,6 +70,7 @@
"@testing-library/react": "^14.2.1",
"@testing-library/user-event": "^14.5.2",
"@types/lodash.isempty": "^4.4.9",
"@types/lodash.range": "^3.2.9",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"@typescript-eslint/eslint-plugin": "^6.14.0",
Expand All @@ -86,7 +85,6 @@
"eslint-plugin-testing-library": "^6.2.0",
"jsdom": "^24.0.0",
"json-schema-to-ts": "^3.0.0",
"lodash.isempty": "^4.4.0",
"prettier": "3.2.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -95,5 +93,9 @@
"typescript": "^5.2.2",
"vite": "^5.0.12",
"vitest": "^1.2.2"
},
"dependencies": {
"lodash.isempty": "^4.4.0",
"lodash.range": "^3.2.0"
}
}
26 changes: 22 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions src/controls/BooleanControl.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { test, expect, vi } from "vitest"
import { screen } from "@testing-library/react"
import { screen, waitFor } from "@testing-library/react"
import { userEvent } from "@testing-library/user-event"
import { render } from "../common/test-render"

Expand Down Expand Up @@ -39,19 +39,19 @@ test("handles onChange event correctly", async () => {

await userEvent.click(checkbox)
expect(checkbox).toBeChecked()
// FYI the calls to updateData lag behind the actual checkbox state. Not sure why.
// It could be the difference between json-forms handleChange(path, value) and the onChange event.
expect(updateData).toHaveBeenLastCalledWith({
data: { name: false },
errors: [],
})
await waitFor(() =>
expect(updateData).toHaveBeenLastCalledWith({
data: { name: true },
errors: [],
}),
)

await userEvent.click(checkbox)
expect(checkbox).not.toBeChecked()
expect(updateData).toHaveBeenLastCalledWith({
data: { name: true },
errors: [],
})

expect(updateData).toBeCalledTimes(2)
await waitFor(() =>
expect(updateData).toHaveBeenLastCalledWith({
data: { name: false },
errors: [],
}),
)
})
160 changes: 160 additions & 0 deletions src/controls/ObjectArrayControl.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { test, expect } from "vitest"
import { screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { render } from "../common/test-render"

import {
objectArrayControlUISchema,
objectArrayControlJsonSchema,
objectArrayControlUISchemaWithIcons,
objectArrayControlJsonSchemaWithRequired,
} from "../testSchemas/objectArraySchema"

test("ObjectArrayControl renders without any data", async () => {
render({
schema: objectArrayControlJsonSchema,
uischema: objectArrayControlUISchema,
})
await screen.findByRole("button")
await screen.findByText("Add Assets")
await screen.findByText("No data")
})

test.each([
[objectArrayControlJsonSchema, false],
[objectArrayControlJsonSchemaWithRequired, true],
])(
"ObjectArrayControl renders disabled remove button with one element if required",
async (schema, should_be_disabled) => {
render({
schema: schema,
uischema: objectArrayControlUISchema,
data: { assets: [{ asset: "my asset" }] },
})
await screen.findByText("Add Assets")
await screen.findByDisplayValue("my asset")
//note: the text is within a span in the <button>
const removeButton = await screen.findByRole("button", { name: "Delete" })
expect(removeButton).toHaveProperty("disabled", should_be_disabled)
},
)

test.each([
[objectArrayControlJsonSchema],
[objectArrayControlJsonSchemaWithRequired],
])(
"ObjectArrayControl renders enabled remove buttons with multiple elements",
async (schema) => {
render({
schema: schema,
uischema: objectArrayControlUISchema,
data: { assets: [{ asset: "my asset" }, { asset: "my other asset" }] },
})
await screen.findByText("Add Assets")
await screen.findByDisplayValue("my asset")
await screen.findByDisplayValue("my other asset")
const removeButtons = await screen.findAllByRole("button", {
name: "Delete",
})
expect(removeButtons).toHaveLength(2)
removeButtons.forEach((removeButton) => {
expect(removeButton).toHaveProperty("disabled", false)
})
},
)

test("ObjectArrayControl correctly appends to the list with add button", async () => {
let data = { assets: [] }
const user = userEvent.setup()
render({
schema: objectArrayControlJsonSchema,
uischema: objectArrayControlUISchema,
data: data,
onChange: (result) => {
data = result.data
},
})
await user.click(screen.getByRole("button", { name: "Add Assets" }))
const newAsset = await screen.findByLabelText("Asset")
await user.type(newAsset, "new")
await screen.findByDisplayValue("new")
await waitFor(() => {
expect(data).toEqual({
assets: [{ asset: "new" }],
})
})
})

test("ObjectArrayControl correctly removes from the list with remove button", async () => {
let data = {
assets: [
{ asset: "my asset" },
{ asset: "remove me!" },
{ asset: "my other asset" },
],
}
const user = userEvent.setup()
render({
schema: objectArrayControlJsonSchema,
uischema: objectArrayControlUISchema,
data: data,
onChange: (result) => {
data = result.data
},
})
await screen.findByDisplayValue("my asset")
await screen.findByDisplayValue("remove me!")
await screen.findByDisplayValue("my other asset")
const removeButtons = await screen.findAllByRole("button", { name: "Delete" })
expect(removeButtons).toHaveLength(3)
await user.click(removeButtons[1])
await waitFor(() => {
expect(data).toEqual({
assets: [{ asset: "my asset" }, { asset: "my other asset" }],
})
})
const updatedRemoveButtons = await screen.findAllByRole("button", {
name: "Delete",
})
expect(updatedRemoveButtons).toHaveLength(2)
TrangPham marked this conversation as resolved.
Show resolved Hide resolved
await screen.findByDisplayValue("my asset")
await screen.findByDisplayValue("my other asset")
})

test("ObjectArrayControl renders with overwritten icons and does not allow overwriting onClick", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does overwritten icons mean, exactly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:
Screenshot 2024-03-18 at 7 40 28 PM

const user = userEvent.setup()
let data = { assets: [] }
render({
schema: objectArrayControlJsonSchema,
uischema: objectArrayControlUISchemaWithIcons,
data: data,
onChange: (result) => {
data = result.data
},
})
// Add button text is overwritten and has the correct icon
await screen.findByText("Add more items")
await screen.findByLabelText("plus-circle") // fyi: aria-label is "plus-circle"

// Check that the onClick handler is not overwritten on Add button
await user.click(await screen.findByText("Add more items"))
await screen.findByDisplayValue("")
await waitFor(() => {
expect(data).toEqual({
assets: [{}],
})
})

// Delete button text is overwritten and has the correct icon
await screen.findByText("Destroy me!")
await screen.findByLabelText("delete") // fyi: aria-label is "delete"

// Check that the onClick handler is not overwritten on Delete button
const deleteButton = await screen.findByText("Destroy me!")
await user.click(deleteButton)
await waitFor(() => {
expect(data).toEqual({
assets: [],
})
})
})
Loading
Loading