From 8c63295043d730e55b6fae030cf6d2769eeb1b66 Mon Sep 17 00:00:00 2001 From: Jackson Morgan Date: Tue, 19 Aug 2025 23:53:11 -0400 Subject: [PATCH] Complete useChange hooks --- .../src/ConnectedLdoTransactionDataset.ts | 8 +-- .../src/trackingProxy/TrackingProxyContext.ts | 2 - .../src/trackingProxy/TrackingSubjectProxy.ts | 2 - .../src/trackingProxy/createTrackingProxy.ts | 1 - .../test/mocks/MockConnectedPlugin.ts | 8 +-- packages/connected/test/mocks/MockResource.ts | 24 +++++--- .../src/methods/change/useChangeDataset.ts | 5 -- .../methods/change/useChangeMatchObject.ts | 31 +++++++---- .../methods/change/useChangeMatchSubject.ts | 33 ++++++----- .../src/methods/change/useChangeSubject.ts | 3 - packages/react/src/methods/useSubject.ts | 2 - packages/react/temptest/src/App.tsx | 4 -- packages/react/test/useLdoForm.test.tsx | 55 ++++++++++++------- packages/subscribable-dataset/src/util.ts | 2 - 14 files changed, 95 insertions(+), 85 deletions(-) diff --git a/packages/connected/src/ConnectedLdoTransactionDataset.ts b/packages/connected/src/ConnectedLdoTransactionDataset.ts index 8e204b9..f0d5b67 100644 --- a/packages/connected/src/ConnectedLdoTransactionDataset.ts +++ b/packages/connected/src/ConnectedLdoTransactionDataset.ts @@ -96,13 +96,6 @@ export class ConnectedLdoTransactionDataset this.instanceId = ConnectedLdoTransactionDataset.nextId++; } - add(quad: Quad): this { - console.log("Adding"); - super.add(quad); - console.log(this); - return this; - } - getResource< Name extends Plugins[number]["name"], Plugin extends Extract, @@ -231,6 +224,7 @@ export class ConnectedLdoTransactionDataset ); // If one has errored, return error + console.log(results); const errors = ( results.map((result) => result[2]) as (SuccessResult | ErrorResult)[] ).filter((result): result is ErrorResult => result.isError); diff --git a/packages/connected/src/trackingProxy/TrackingProxyContext.ts b/packages/connected/src/trackingProxy/TrackingProxyContext.ts index 39f8aeb..277bb38 100644 --- a/packages/connected/src/trackingProxy/TrackingProxyContext.ts +++ b/packages/connected/src/trackingProxy/TrackingProxyContext.ts @@ -41,7 +41,6 @@ export class TrackingProxyContext extends ProxyContext { listener: nodeEventListener, ) { super(options); - console.log("trackingProxyContextDataset", options.dataset); this.subscribableDataset = options.dataset; this.listener = listener; } @@ -52,7 +51,6 @@ export class TrackingProxyContext extends ProxyContext { if (!listeners.includes(this.listener)) { this.subscribableDataset.on(eventName, this.listener); } - console.log("Added Listener", this.subscribableDataset); } protected createNewSubjectProxy(node: NamedNode | BlankNode): SubjectProxy { diff --git a/packages/connected/src/trackingProxy/TrackingSubjectProxy.ts b/packages/connected/src/trackingProxy/TrackingSubjectProxy.ts index 16fdded..98d11dc 100644 --- a/packages/connected/src/trackingProxy/TrackingSubjectProxy.ts +++ b/packages/connected/src/trackingProxy/TrackingSubjectProxy.ts @@ -24,7 +24,6 @@ export function createTrackingSubjectProxy( key: string | symbol, receiver, ) => { - console.log("Should be calling this get function"); const subject = target["@id"]; const rdfTypes = proxyContext.getRdfType(subject); if (typeof key === "symbol") { @@ -32,7 +31,6 @@ export function createTrackingSubjectProxy( } else if (key === "@id") { proxyContext.addListener([subject, null, null, null]); } else if (!proxyContext.contextUtil.isSet(key, rdfTypes)) { - console.log("Should be registering here!!!!", key); const predicate = namedNode( proxyContext.contextUtil.keyToIri(key, rdfTypes), ); diff --git a/packages/connected/src/trackingProxy/createTrackingProxy.ts b/packages/connected/src/trackingProxy/createTrackingProxy.ts index dddf855..16add22 100644 --- a/packages/connected/src/trackingProxy/createTrackingProxy.ts +++ b/packages/connected/src/trackingProxy/createTrackingProxy.ts @@ -25,7 +25,6 @@ export function createTrackingProxyBuilder( // Rebuild the LdoBuilder from scratch to inject TrackingProxyContext const contextUtil = new ContextUtil(shapeType.context); - console.log("Creating proxy", dataset); const proxyContext = new TrackingProxyContext( { dataset, diff --git a/packages/connected/test/mocks/MockConnectedPlugin.ts b/packages/connected/test/mocks/MockConnectedPlugin.ts index 12678b5..c2ca09d 100644 --- a/packages/connected/test/mocks/MockConnectedPlugin.ts +++ b/packages/connected/test/mocks/MockConnectedPlugin.ts @@ -25,13 +25,13 @@ export const mockConnectedPlugin: MockConnectedPlugin = { getResource: function ( uri: string, - _context: ConnectedContext, + context: ConnectedContext, ): MockResource { - return new MockResource(uri); + return new MockResource(uri, context); }, - createResource: async function (): Promise { - return new MockResource(v4()); + createResource: async function (context): Promise { + return new MockResource(v4(), context); }, isUriValid: function (uri: string): uri is string { diff --git a/packages/connected/test/mocks/MockResource.ts b/packages/connected/test/mocks/MockResource.ts index 91a4b40..7af0951 100644 --- a/packages/connected/test/mocks/MockResource.ts +++ b/packages/connected/test/mocks/MockResource.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import EventEmitter from "events"; +import type { ConnectedContext } from "../../src/index.js"; import { Unfetched, type ConnectedResult, @@ -9,8 +10,10 @@ import { } from "../../src/index.js"; import type { DatasetChanges } from "@ldo/rdf-utils"; import type { ReadSuccess } from "../../src/results/success/ReadSuccess.js"; -import type { UpdateSuccess } from "../../src/results/success/UpdateSuccess.js"; +import { UpdateSuccess } from "../../src/results/success/UpdateSuccess.js"; import { vi } from "vitest"; +import type { MockConnectedPlugin } from "./MockConnectedPlugin.js"; +import type { Quad } from "@rdfjs/types"; export class MockResource extends (EventEmitter as new () => ResourceEventEmitter) @@ -21,10 +24,13 @@ export class MockResource type = "mock" as const; status: ConnectedResult; - constructor(uri: string) { + protected context: ConnectedContext; + + constructor(uri: string, context: ConnectedContext) { super(); this.uri = uri; this.status = new Unfetched(this); + this.context = context; } isLoading = vi.fn<() => boolean>(); @@ -38,12 +44,14 @@ export class MockResource read = vi.fn<() => Promise | ResourceError>>(); readIfUnfetched = vi.fn<() => Promise | ResourceError>>(); - update = - vi.fn< - ( - changes: DatasetChanges, - ) => Promise | ResourceError> - >(); + update = vi.fn< + ( + changes: DatasetChanges, + ) => Promise | ResourceError> + >((changes) => { + this.context.dataset.bulk(changes); + return new UpdateSuccess(this); + }); subscribeToNotifications = vi.fn< diff --git a/packages/react/src/methods/change/useChangeDataset.ts b/packages/react/src/methods/change/useChangeDataset.ts index 01f8e09..55ba4df 100644 --- a/packages/react/src/methods/change/useChangeDataset.ts +++ b/packages/react/src/methods/change/useChangeDataset.ts @@ -32,7 +32,6 @@ export function createUseChangeDataset( specificDataset?: IConnectedLdoDataset, ): useChangeDatasetReturn { const transactionDataset = useMemo(() => { - console.log("Uh oh! Getting another dataset!!!"); return ( specificDataset ?? dataset ).startTransaction() as ConnectedLdoTransactionDataset; @@ -48,10 +47,6 @@ export function createUseChangeDataset( ); const commitData = useCallback>(() => { - const changes = transactionDataset.getChanges(); - console.log("Changes!!!!!"); - console.log(changes.added?.toString()); - console.log(changes.removed?.toString()); return transactionDataset.commitToRemote(); }, [transactionDataset]); diff --git a/packages/react/src/methods/change/useChangeMatchObject.ts b/packages/react/src/methods/change/useChangeMatchObject.ts index e8513e5..add1195 100644 --- a/packages/react/src/methods/change/useChangeMatchObject.ts +++ b/packages/react/src/methods/change/useChangeMatchObject.ts @@ -1,11 +1,12 @@ import { useCallback, useMemo } from "react"; import type { ConnectedLdoDataset, ConnectedPlugin } from "@ldo/connected"; import { createUseChangeDataset } from "./useChangeDataset.js"; -import type { LdoBase, LdSet, ShapeType } from "@ldo/ldo"; +import { write, type LdoBase, type LdSet, type ShapeType } from "@ldo/ldo"; import type { QuadMatch } from "@ldo/rdf-utils"; import type { UseMatchObjectOptions } from "../useMatchObject.js"; import { createUseMatchObject } from "../useMatchObject.js"; import type { useChangeReturn, useChangeSetData } from "./types.js"; +import { createProxyInteractOptions } from "@ldo/jsonld-dataset-proxy"; /** * @internal @@ -21,7 +22,7 @@ export function createUseChangeMatchObject( /** * Returns a list of matched objects that can be modified and committed */ - return function useChangeSubject( + return function useChangeMatchObject( shapeType: ShapeType, subject?: QuadMatch[0] | string, predicate?: QuadMatch[1] | string, @@ -32,28 +33,34 @@ export function createUseChangeMatchObject( options?.dataset, ); - const ldObjects = useMatchObject(shapeType, subject, predicate, graph, { + const ldObject = useMatchObject(shapeType, subject, predicate, graph, { dataset: transactionDataset, }); - const setData = useCallback, Plugins>>( - (writeResource, changer, _other) => { + const setData = useCallback>( + (writeResource, changer, otherType?) => { setDataset((dataset) => { - const ldSet = dataset - .usingType(shapeType) - .write(writeResource.uri) - .matchObject(subject, predicate, graph); + const ldObject = otherType + ? write(writeResource.uri).usingCopy( + createProxyInteractOptions("dataset", dataset).usingCopy( + otherType, + )[0], + )[0] + : dataset + .usingType(shapeType) + .write(writeResource.uri) + .matchObject(subject, predicate, graph); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - changer(ldSet); + changer(ldObject); }); }, [setDataset, subject, predicate, graph, shapeType], ); return useMemo( - () => [ldObjects, setData, commitData], - [ldObjects, setData, commitData], + () => [ldObject, setData, commitData], + [ldObject, setData, commitData], ); }; } diff --git a/packages/react/src/methods/change/useChangeMatchSubject.ts b/packages/react/src/methods/change/useChangeMatchSubject.ts index 206fc8f..df49c32 100644 --- a/packages/react/src/methods/change/useChangeMatchSubject.ts +++ b/packages/react/src/methods/change/useChangeMatchSubject.ts @@ -1,13 +1,14 @@ import { useCallback, useMemo } from "react"; import type { ConnectedLdoDataset, ConnectedPlugin } from "@ldo/connected"; import { createUseChangeDataset } from "./useChangeDataset.js"; -import type { LdoBase, LdSet, ShapeType } from "@ldo/ldo"; +import { write, type LdoBase, type LdSet, type ShapeType } from "@ldo/ldo"; import type { QuadMatch } from "@ldo/rdf-utils"; import type { useChangeReturn, useChangeSetData } from "./types.js"; import { createUseMatchSubject, type UseMatchSubjectOptions, } from "../useMatchSubject.js"; +import { createProxyInteractOptions } from "@ldo/jsonld-dataset-proxy"; /** * @internal @@ -23,7 +24,7 @@ export function createUseChangeMatchSubject( /** * Returns a list of matched subjects that can be modified and committed */ - return function useChangeSubject( + return function useChangeMatchSubject( shapeType: ShapeType, predicate?: QuadMatch[1] | string, object?: QuadMatch[2] | string, @@ -34,28 +35,34 @@ export function createUseChangeMatchSubject( options?.dataset, ); - const ldSubjects = useMatchSubject(shapeType, predicate, object, graph, { + const ldObject = useMatchSubject(shapeType, predicate, object, graph, { dataset: transactionDataset, }); - const setData = useCallback, Plugins>>( - (writeResource, changer, _other) => { + const setData = useCallback>( + (writeResource, changer, otherType?) => { setDataset((dataset) => { - const ldSet = dataset - .usingType(shapeType) - .write(writeResource.uri) - .matchSubject(predicate, object, graph); + const ldObject = otherType + ? write(writeResource.uri).usingCopy( + createProxyInteractOptions("dataset", dataset).usingCopy( + otherType, + )[0], + )[0] + : dataset + .usingType(shapeType) + .write(writeResource.uri) + .matchSubject(predicate, object, graph); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - changer(ldSet); + changer(ldObject); }); }, - [setDataset, object, predicate, graph, shapeType], + [setDataset, predicate, object, graph, shapeType], ); return useMemo( - () => [ldSubjects, setData, commitData], - [ldSubjects, setData, commitData], + () => [ldObject, setData, commitData], + [ldObject, setData, commitData], ); }; } diff --git a/packages/react/src/methods/change/useChangeSubject.ts b/packages/react/src/methods/change/useChangeSubject.ts index da61040..babcbb5 100644 --- a/packages/react/src/methods/change/useChangeSubject.ts +++ b/packages/react/src/methods/change/useChangeSubject.ts @@ -55,7 +55,6 @@ export function createUseChangeSubject( const setData = useCallback>( (writeResource, changer, otherType?) => { - console.log("Setting data"); if (!subject) return; setDataset((dataset) => { const ldObject = otherType @@ -76,8 +75,6 @@ export function createUseChangeSubject( [setDataset, subject, shapeType], ); - console.log("This is ldObject", ldObject); - return useMemo( () => [ldObject, setData, commitData], [ldObject, setData, commitData], diff --git a/packages/react/src/methods/useSubject.ts b/packages/react/src/methods/useSubject.ts index 53bc6f2..e198b03 100644 --- a/packages/react/src/methods/useSubject.ts +++ b/packages/react/src/methods/useSubject.ts @@ -50,8 +50,6 @@ export function createUseSubject( subject?: string | SubjectNode, options?: UseSubjectOptions, ): Type | undefined { - console.log("options", options); - const fromSubject = useCallback( (builder: LdoBuilder) => { if (!subject) return; diff --git a/packages/react/temptest/src/App.tsx b/packages/react/temptest/src/App.tsx index 025959e..94e2f9e 100644 --- a/packages/react/temptest/src/App.tsx +++ b/packages/react/temptest/src/App.tsx @@ -14,8 +14,6 @@ const FormTest: FunctionComponent = () => { "Example0", ); - console.log("This is data", data); - return (

Form

@@ -24,7 +22,6 @@ const FormTest: FunctionComponent = () => { onSubmit={async (e) => { e.preventDefault(); const result = await commitData(); - console.log(result); }} > {/* Primary name field */} @@ -33,7 +30,6 @@ const FormTest: FunctionComponent = () => { value={data?.fn ?? ""} onChange={(e) => { setData(randomResource, (profile) => { - console.log("Inside changer"); profile.fn = e.target.value; }) }} diff --git a/packages/react/test/useLdoForm.test.tsx b/packages/react/test/useLdoForm.test.tsx index 86c60b5..87fbea7 100644 --- a/packages/react/test/useLdoForm.test.tsx +++ b/packages/react/test/useLdoForm.test.tsx @@ -2,7 +2,7 @@ /// import { useResource, useChangeSubject, useSubject } from "./mockLdoMethods.js"; import type { FunctionComponent } from "react"; -import React from "react"; +import React, { useState } from "react"; import { describe, it, expect, beforeEach } from "vitest"; import { render, screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; @@ -15,34 +15,35 @@ import "@testing-library/jest-dom/vitest"; * accessible labels, and committed data shown via useSubject. */ const FormTest: FunctionComponent = () => { - const randomResource = useResource("random"); + const randomResource = useResource("http://example.com/resource.ttl"); const submittedData = useSubject(SolidProfileShapeShapeType, "Example0"); + const [count, setCount] = useState(1); + const [data, setData, commitData] = useChangeSubject( SolidProfileShapeShapeType, - randomResource, "Example0", ); return (

Form

+
{ e.preventDefault(); - const result = await commitData(); - console.log(result); + await commitData(); }} > {/* Primary name field */} - setData((profile) => { + onChange={(e) => { + setData(randomResource, (profile) => { profile.fn = e.target.value; - }) - } + }); + }} /> {/* Friends */} @@ -53,15 +54,19 @@ const FormTest: FunctionComponent = () => { aria-label={`Friend name for ${person["@id"]}`} value={person?.fn ?? ""} onChange={(e) => - setData((p) => { - p.fn = e.target.value; - }, person) + setData( + randomResource, + (p) => { + p.fn = e.target.value; + }, + person, + ) } /> - +

@@ -190,15 +195,25 @@ describe("useChangeSubject", () => { expect(screen.queryByTestId("friend-Example2")).not.toBeInTheDocument(); // 5) Submit -> committed data reflects Example0 + Example1 only - await user.click(screen.getByRole("button", { name: /submit/i })); + await user.click(screen.getByRole("button", { name: /Submit/i })); // Form retained its values expect(nameInput).toHaveValue("Example0"); expect(friend1Input).toHaveValue("Example1"); expect(screen.queryByTestId("friend-Example2")).not.toBeInTheDocument(); - // Committed view updated - expect(submittedName).toHaveTextContent("Name: Example0"); + // Wait for the submitted data to appear in the document before asserting + const submittedSection2 = screen.getByRole("region", { + name: /submitted data/i, + }); + const submittedName2 = + within(submittedSection2).getByTestId("submitted-name"); + + // Use `findByText` from the `within` helper to wait for the change + await within(submittedSection2).findByText("Name: Example0"); + + // Now that we've successfully waited, we can safely make our assertions + expect(submittedName2).toHaveTextContent("Name: Example0"); const itemsAfterSubmit = within(submittedList).getAllByRole("listitem"); expect(itemsAfterSubmit).toHaveLength(1); expect(itemsAfterSubmit[0]).toHaveTextContent( diff --git a/packages/subscribable-dataset/src/util.ts b/packages/subscribable-dataset/src/util.ts index c93c7fc..c0048f1 100644 --- a/packages/subscribable-dataset/src/util.ts +++ b/packages/subscribable-dataset/src/util.ts @@ -11,10 +11,8 @@ export function updateDatasetInBulk( dataset: Dataset, datasetChanges: DatasetChanges, ) { - console.log("Update Dataset In Bulk"); // eslint-disable-next-line @typescript-eslint/no-explicit-any if ((dataset as any).bulk) { - console.log("Updating in bulk"); (dataset as IBulkEditableDataset).bulk(datasetChanges); } else { if (datasetChanges.added) {