Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 8 additions & 2 deletions bun.lock

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

5 changes: 5 additions & 0 deletions lib/components/SchematicViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export const SchematicViewer = ({
)

const [showGridInternal, setShowGridInternal] = useState(false)
const [showWarnings, setShowWarnings] = useState(false)
const showGrid = debugGrid || showGridInternal
const [isInteractionEnabled, setIsInteractionEnabled] = useState<boolean>(
!clickToInteractEnabled,
Expand Down Expand Up @@ -421,6 +422,7 @@ export const SchematicViewer = ({
width: containerWidth,
height: containerHeight || 720,
drawPorts: showSchematicPortsInternal,
shouldDrawWarnings: showWarnings,
schematicSheetId: activeSheetId,
grid: !showGrid
? undefined
Expand All @@ -437,6 +439,7 @@ export const SchematicViewer = ({
containerWidth,
containerHeight,
showGrid,
showWarnings,
showSchematicPortsInternal,
activeSheetId,
])
Expand Down Expand Up @@ -643,6 +646,8 @@ export const SchematicViewer = ({
)
}
}}
showWarnings={showWarnings}
onToggleWarnings={setShowWarnings}
showGrid={showGrid}
onToggleGrid={setShowGridInternal}
/>
Expand Down
19 changes: 19 additions & 0 deletions lib/components/ViewMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ interface ViewMenuProps {
onToggleGroups: (show: boolean) => void
showGrid: boolean
onToggleGrid: (show: boolean) => void
showWarnings: boolean
onToggleWarnings: (show: boolean) => void
showPorts: boolean
onTogglePorts: (show: boolean) => void
}
Expand Down Expand Up @@ -103,6 +105,8 @@ export const ViewMenu = ({
onToggleGrid,
showPorts,
onTogglePorts,
showWarnings,
onToggleWarnings,
}: ViewMenuProps) => {
const hasGroups = useMemo(() => {
if (!circuitJson || circuitJson.length === 0) return false
Expand Down Expand Up @@ -217,6 +221,21 @@ export const ViewMenu = ({
<span>Show Grid</span>
</DropdownMenu.Item>

<DropdownMenu.CheckboxItem
className="sv-vm-item"
style={itemStyles}
checked={showWarnings}
onCheckedChange={onToggleWarnings}
onSelect={(event) => event.preventDefault()}
>
<span style={iconSlotStyles}>
<DropdownMenu.ItemIndicator>
<CheckIcon />
</DropdownMenu.ItemIndicator>
</span>
<span>Show Warnings</span>
</DropdownMenu.CheckboxItem>

<DropdownMenu.Separator style={separatorStyles} />

<div
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@tscircuit/circuit-json-util": "^0.0.108",
"circuit-json": "^0.0.479",
"circuit-to-svg": "^0.0.410",
"circuit-to-svg": "^0.0.416",
"debug": "^4.4.0",
"fflate": "^0.8.3",
"performance-now": "^2.1.0",
Expand Down
105 changes: 105 additions & 0 deletions tests/schematic-context-menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,32 @@ const installDom = () => {
MouseEvent: globalThis.MouseEvent,
MutationObserver: globalThis.MutationObserver,
Node: globalThis.Node,
ResizeObserver: globalThis.ResizeObserver,
getComputedStyle: globalThis.getComputedStyle,
}

class TestResizeObserver {
observe() {}
disconnect() {}
}
dom.window.Element.prototype.getBoundingClientRect = () =>
({
x: 0,
y: 0,
left: 0,
top: 0,
right: 800,
bottom: 600,
width: 800,
height: 600,
toJSON: () => {},
}) as DOMRect
Object.assign(dom.window, {
requestAnimationFrame: (callback: FrameRequestCallback) =>
dom.window.setTimeout(() => callback(Date.now()), 0),
cancelAnimationFrame: (id: number) => dom.window.clearTimeout(id),
})

Object.assign(globalThis, {
window: dom.window,
document: dom.window.document,
Expand All @@ -32,6 +55,7 @@ const installDom = () => {
MouseEvent: dom.window.MouseEvent,
MutationObserver: dom.window.MutationObserver,
Node: dom.window.Node,
ResizeObserver: TestResizeObserver,
getComputedStyle: dom.window.getComputedStyle,
IS_REACT_ACT_ENVIRONMENT: true,
})
Expand Down Expand Up @@ -96,6 +120,8 @@ test("the context menu toggles schematic ports", async () => {
onTogglePorts={setShowPorts}
showGroups={false}
onToggleGroups={() => {}}
showWarnings={false}
onToggleWarnings={() => {}}
showGrid={false}
onToggleGrid={() => {}}
/>
Expand Down Expand Up @@ -235,3 +261,82 @@ test("a long press opens the context menu on touch devices", async () => {
restore()
}
})

test("the warnings menu toggles rendered callouts with mouse and keyboard", async () => {
const { dom, restore } = installDom()
const reactRoot = createRoot(document.getElementById("root")!)
const { SchematicViewer } = await import("../lib/components/SchematicViewer")
const message = "This component has a manual edit conflict"
const circuitJson = [
...circuitJsonWithPort,
{
type: "schematic_manual_edit_conflict_warning",
schematic_manual_edit_conflict_warning_id: "warning_1",
schematic_component_id: "schematic_component_1",
message,
},
]

try {
await act(async () =>
reactRoot.render(<SchematicViewer circuitJson={circuitJson} />),
)
expect(document.querySelector("svg")).not.toBeNull()
expect(document.querySelector(".schematic-warning")).toBeNull()

const component = document.querySelector(
'[data-schematic-component-id="schematic_component_1"]',
)!
await act(async () => {
component.dispatchEvent(
new dom.window.MouseEvent("mousedown", {
bubbles: true,
button: 2,
clientX: 100,
clientY: 100,
}),
)
component.dispatchEvent(
new dom.window.MouseEvent("contextmenu", {
bubbles: true,
cancelable: true,
button: 2,
clientX: 100,
clientY: 100,
}),
)
})
await act(
() => new Promise<void>((resolve) => dom.window.setTimeout(resolve, 0)),
)

const item = document.querySelector('[role="menuitemcheckbox"]')!
expect(item.textContent).toBe("Show Warnings")
expect(item.getAttribute("aria-checked")).toBe("false")
await act(async () => {
item.dispatchEvent(new dom.window.MouseEvent("click", { bubbles: true }))
})
expect(item.getAttribute("aria-checked")).toBe("true")
expect(
document.querySelector(".schematic-warning text")?.textContent,
).toContain(message)
expect(
document.querySelector('[data-warning-reference="target"]'),
).not.toBeNull()

await act(async () => {
item.dispatchEvent(
new dom.window.KeyboardEvent("keydown", {
key: "Enter",
bubbles: true,
}),
)
})
expect(item.getAttribute("aria-checked")).toBe("false")
expect(document.querySelector(".schematic-warning")).toBeNull()
} finally {
await act(async () => reactRoot.unmount())
await new Promise<void>((resolve) => dom.window.setTimeout(resolve, 0))
restore()
}
})
Loading