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

Support structs variables #8

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
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
98 changes: 67 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ See [Examples.tsx](./src/example/Example.tsx) for a complete example, and see [E
A basic config definition:

```typescript
export const BASIC_CONFIG_DEFINITION = [
const BASIC_CONFIG_DEFINITION = [
{
fieldId: "stringField",
field: {
Expand Down Expand Up @@ -96,45 +96,81 @@ Here is an example React component that shows how to call `useWorkshopContext` w
const ExampleComponent = () => {
const workshopContext = useWorkshopContext(BASIC_CONFIG_DEFINITION);

if (isAsyncValue_Loading(workshopContext)) {
// Render a loading state
} else if (isAsyncValue_Loaded(workshopContext)) {
// Must explicitly declare type for the loaded context value
const loadedWorkshopContext: IWorkshopContext<typeof BASIC_CONFIG_DEFINITION> = workshopContext.value;

const { stringField, workshopEvent, listOfField } = loadedWorkshopContext;
return visitLoadingState(workshopContext, {
loading: () => <>...Render a loading state</>,
succeeded: (
// Must use <typeof ...> to explicitly delare type for loaded context value
loadedWorkshopContext: IWorkshopContext<typeof BASIC_CONFIG_DEFINITION>
) => (
<LoadedExampleComponent loadedWorkshopContext={loadedWorkshopContext} />
),
reloading: (_reloadingContext) => <>...Render a reloading state</>,
failed: (_error) => <>...Render an error state</>,
});
};

// Examples of retrieving single field values.
const stringValue: IAsyncValue<string | undefined> = stringField.fieldValue;
const LoadedExampleComponent: React.FC<{
// Must use <typeof ...> to explicitly delare type for loaded context value
loadedWorkshopContext: IWorkshopContext<typeof BASIC_CONFIG_DEFINITION>;
}> = (props) => {
const { stringField, workshopEvent, listOfField } =
props.loadedWorkshopContext;

// Examples of retrieving listOf field values.
listOfField.forEach(listItem => {
const booleanListValue: IAsyncValue<boolean[] | undefined> = listItem.booleanListField.fieldValue;
});
// Example of retrieving single field values.
const stringValue: IAsyncValue<string | undefined> = stringField.fieldValue;

// Examples of setting single field values.
// Examples of setting a single field value
const changeStringFieldValue = React.useCallback(() => {
stringField.setLoading();
stringField.setLoadedValue("Hello world!");
stringField.setReloadingValue("Hello world is reloading.");
stringField.setFailedWithError("Hello world failed to load.");
}, [stringField]);

// Examples of setting listOf field values.
listOfField.forEach((listItem, index) => {
listItem.booleanListField.setLoading();
listItem.booleanListField.setLoadedValue([true, false]);
listItem.booleanListField.setReloadingValue([false, true]);
listItem.booleanListField.setFailedWithError(`Failed to load on listOf layer ${index}`);
});


// Example of executing event. Takes a React MouseEvent, or undefined if not applicable
// Example of executing event.
const executeWorkshopEvent = React.useCallback(() => {
// Takes a React MouseEvent, or undefined if not applicable
workshopEvent.executeEvent(undefined);


return <div>Render something here.</div>;
} else if (isAsyncValue_FailedLoading(workshopContext)) {
// Render a failure state
}
}, [workshopEvent]);

// Examples of setting a single field value inside listOf field values
const executeListOfFieldChange = React.useCallback(
(index: number) => () => {
if (index < listOfField.length) {
listOfField[index]?.booleanListField.setLoading();
listOfField[index]?.booleanListField.setLoadedValue([true, false]);
listOfField[index]?.booleanListField.setReloadingValue([false, true]);
listOfField[index]?.booleanListField.setFailedWithError(
`Failed to load on listOf layer ${index}`
);
}
},
[listOfField]
);

return (
<div>
{JSON.stringify(stringValue)}
<button onClick={changeStringFieldValue}>
Click me to change stringField's value!
</button>
<br />
<button onClick={executeWorkshopEvent}>
Click me to execute a workshop event!
</button>
<br />
{listOfField.map((listItem, index) => {
return (
<>
{JSON.stringify(listItem.booleanListField.fieldValue)}
<button onClick={executeListOfFieldChange(index)}>
Click me to change a listOfField's value for index {index}!
</button>
</>
);
})}
</div>
);
};
```

Expand Down
39 changes: 28 additions & 11 deletions src/createDefaultConfigValueMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
* limitations under the License.
*/


import { asyncValueLoaded, IAsyncValue, IConfigDefinition } from "./types";
import { asyncValueLoaded, IConfigDefinition } from "./types";
import { assertNever, formatDate } from "./utils";
import { IConfigValueMap, IVariableType_WithDefaultValue, IVariableValue } from "./internal";
import {
IConfigValueMap,
IVariableType_WithDefaultValue,
IVariableValue,
StructValue,
} from "./internal";

/**
* Takes the configDefinition and pulls out the default values, creating a default config values map.
Expand All @@ -32,8 +36,10 @@ export function createDefaultConfigValueMap(
case "inputOutput":
configValueMap[configField.fieldId] = {
type: "single",
value: variableTypeWithDefaultValueToValue(
configField.field.fieldValue.variableType
value: asyncValueLoaded(
variableTypeWithDefaultValueToValue(
configField.field.fieldValue.variableType
)
),
};
return;
Expand Down Expand Up @@ -65,13 +71,24 @@ export function createDefaultConfigValueMap(

function variableTypeWithDefaultValueToValue(
variableType: IVariableType_WithDefaultValue
): IAsyncValue<IVariableValue | undefined> {
): IVariableValue | undefined {
// For date and date-list variable types, need to convert from Date to string and Date[] to string[]
// As we will save date values as strings in format "yyyy-mm-dd"
if (variableType.type === "date" && variableType.defaultValue != null) {
return asyncValueLoaded(formatDate(variableType.defaultValue));
} else if (variableType.type === "date-list" && variableType.defaultValue != null) {
return asyncValueLoaded(variableType.defaultValue.map(formatDate));
}
return asyncValueLoaded(variableType.defaultValue);
return formatDate(variableType.defaultValue);
} else if (
variableType.type === "date-list" &&
variableType.defaultValue != null
) {
return variableType.defaultValue.map(formatDate);
} else if (variableType.type === "struct") {
const structFields = variableType.structFieldTypes.reduce((acc, structFieldType) => {
acc[structFieldType.fieldId] = variableTypeWithDefaultValueToValue(structFieldType.fieldType);
return acc;
}, {} as StructValue["structFields"]);
return {
structFields,
};
}
return variableType.defaultValue;
}
105 changes: 70 additions & 35 deletions src/example/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ limitations under the License.
*/
import React from "react";
import { COMPREHENSIVE_EXAMPLE_CONFIG } from "./ExampleConfig";
import {
IAsyncValue,
visitLoadingState,
} from "../types/loadingState";
import { IAsyncValue, visitLoadingState } from "../types/loadingState";
import { IWorkshopContext } from "../types/workshopContext";
import { useWorkshopContext } from "../";
import { ObjectSetLocators } from "../types";
Expand All @@ -31,9 +28,13 @@ export const Example = () => {
// Use a visitor function to render based on the async status of the workshop context object
return visitLoadingState(workshopContext, {
loading: () => <>Loading...</>,
succeeded: loadedContext => <LoadedComprehensiveExample loadedWorkshopContext={loadedContext} />,
reloading: _reloadingContext => <>Reloading...</>,
failed: _error => <>Error...</>,
succeeded: (
loadedContext: IWorkshopContext<typeof COMPREHENSIVE_EXAMPLE_CONFIG>
) => <LoadedComprehensiveExample loadedWorkshopContext={loadedContext} />,
reloading: (
_reloadingContext: IWorkshopContext<typeof COMPREHENSIVE_EXAMPLE_CONFIG>
) => <>Reloading...</>,
failed: (_error) => <>Error...</>,
});
};

Expand All @@ -49,6 +50,7 @@ const LoadedComprehensiveExample: React.FC<{
numberField,
dateField,
timestampField,
structField,
stringListField,
objectSetField,
event,
Expand All @@ -72,20 +74,29 @@ const LoadedComprehensiveExample: React.FC<{
const dateFieldValue: IAsyncValue<string | undefined> = dateField.fieldValue;
const timestampFieldValue: IAsyncValue<Date | undefined> =
timestampField.fieldValue;
const structFieldValue: IAsyncValue<
| {
structFields: {
structField1: string | undefined;
structField2: boolean | undefined;
};
}
| undefined
> = structField.fieldValue;

const objectSetFieldValue: IAsyncValue<ObjectSetLocators | undefined> =
objectSetField.fieldValue;
// Example usage of objectSetFieldValue's primary keys to query for objects using osdk's client:
// const primaryKeys: string[] = isAsyncValueLoaded(objectSetFieldValue) ? objectSetFieldValue.value.primaryKeys : [];
// const housesfilteredByPrimaryKey: ObjectSet<RottenTomatoesMovies> = client.ontology.objects.RottenTomatoesMovies.where(query => query.rottenTomatoesLink.containsAnyTerm(primaryKeys.join(" ")));

const stringListFieldValue: IAsyncValue<string[] | undefined> =
const stringListFieldValue: IAsyncValue<string[] | undefined> =
stringListField.fieldValue;
const booleanListFieldValue: IAsyncValue<boolean[] | undefined> =
booleanListField.fieldValue;
const numberListFieldValue: IAsyncValue<number[] | undefined> =
numberListField.fieldValue;
// date arrays are stored as a string array with every entry in the format "yyyy-mm-dd"
// date arrays are stored as a string array with every entry in the format "yyyy-mm-dd"
const dateListFieldValue: IAsyncValue<string[] | undefined> =
dateListField.fieldValue;
const timestampListFieldValue: IAsyncValue<Date[] | undefined> =
Expand All @@ -97,17 +108,36 @@ const LoadedComprehensiveExample: React.FC<{
stringField.setLoading();
stringField.setLoadedValue("Hello world!!!"); // The value takes the config field type, in this case, string
stringField.setReloadingValue("I am reloading..."); // The value takes the config field type, in this case, string
stringField.setFailedWithError("Oh no, an error occurred!"); // Takes string for error message
stringField.setFailedWithError("Oh no, an error occurred with stringField!"); // Takes string for error message

booleanField.setLoading();
booleanField.setLoadedValue(false); // The value takes the config field type, in this case, boolean
booleanField.setReloadingValue(true); // The value takes the config field type, in this case, boolean
booleanField.setFailedWithError("Oh no, an error occurred!"); // Takes string for error message
booleanField.setFailedWithError(
"Oh no, an error occurred with booleanField!"
); // Takes string for error message

dateField.setLoading();
dateField.setLoadedValue(new Date("2024-01-01")); // The value takes the config field type, in this case, Date. Note that the value saved is a string in format "yyyy-mm-dd"
dateField.setReloadingValue(new Date("2024-12-31")); // The value takes the config field type, in this case, Date. Note that the value saved is a string in format "yyyy-mm-dd"
dateField.setFailedWithError("Oh no, an error occurred!"); // Takes string for error message
dateField.setFailedWithError("Oh no, an error occurred with dateField!"); // Takes string for error message

structField.setLoading();
structField.setLoadedValue({
// The value takes the config field type, in this case the struct defined in the config
structFields: {
structField1: "Hello world!",
structField2: true,
},
});
structField.setReloadingValue({
// The value takes the config field type, in this case the struct defined in the config
structFields: {
structField1: "I am reloading...",
structField2: false,
},
});
structField.setFailedWithError("Oh no, an error occurred with structField!"); // Takes string for error message

/**
* Examples of executing an event
Expand Down Expand Up @@ -151,27 +181,32 @@ const LoadedComprehensiveExample: React.FC<{
});
});

return <>
{stringFieldValue}
<br />
{booleanFieldValue}
<br />
{numberFieldValue}
<br />
{dateFieldValue}
<br />
{timestampFieldValue}
<br />
{objectSetFieldValue}
<br />
{stringListFieldValue}
<br />
{booleanListFieldValue}
<br />
{numberListFieldValue}
<br />
{dateListFieldValue}
<br />
{timestampListFieldValue}
</>;
return (
<>
{stringFieldValue}
<br />
{booleanFieldValue}
<br />
{numberFieldValue}
<br />
{dateFieldValue}
<br />
{timestampFieldValue}
<br />
{structFieldValue}
<br />
{objectSetFieldValue}
<br />
{stringListFieldValue}
<br />
{booleanListFieldValue}
<br />
{numberListFieldValue}
<br />
{dateListFieldValue}
<br />
{timestampListFieldValue}
</>
);
};

Loading