From 1b51f41df20f00294618f24d9f8645e3a2ebbc81 Mon Sep 17 00:00:00 2001 From: Raghavan Chandrabalan Date: Mon, 24 Jan 2022 17:00:31 -0500 Subject: [PATCH 1/8] remove any from freezer-js declaration file --- src/freezer-js.d.ts | 62 ++++++++++++++++++++++----------------------- src/state.ts | 4 +-- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/freezer-js.d.ts b/src/freezer-js.d.ts index 4f2b1e6..fb5cf28 100644 --- a/src/freezer-js.d.ts +++ b/src/freezer-js.d.ts @@ -5,29 +5,29 @@ declare module 'freezer-js' { freezeInstances: boolean, } - export type EventDict = E extends {[K in keyof E]: (...args: any[]) => any} ? { + export type EventDict = E extends {[K in keyof E]: (...args: any[]) => unknown} ? { [K in keyof E]: E[K] } : never; // FreezerEvents shortened to FE because it will be repeated a lot! - export type FE> = E & { + export type FE = E & { "update": (state: T, prevState: T) => void, - "beforeAll": (eventName: K, ...args: Parameters) => void, - "afterAll": (eventName: K, ...args: Parameters) => void, + "beforeAll": (eventName: K, ...args: Parameters ? E[K] : never>) => void, + "afterAll": (eventName: K, ...args: Parameters ? E[K] : never>) => void, }; - export type FreezerListener> = { - on: (eventName: K, cb: E[K]) => FreezerListener; - once: (eventName: K, cb: E[K]) => FreezerListener; - off: (eventName: K, cb: E[K]) => FreezerListener; - emit: (eventName: K, ...args: Parameters) => ReturnType; - trigger: (eventName: K, ...args: Parameters) => ReturnType; // deprecated + export type FreezerListener = { + on: >(eventName: K, cb: FE[K]) => FreezerListener; + once: >(eventName: K, cb: FE[K]) => FreezerListener; + off: >(eventName: K, cb: FE[K]) => FreezerListener; + emit: >(eventName: K, ...args: Parameters ? FE[K] : never>) => ReturnType ? FE[K] : never>; + trigger: >(eventName: K, ...args: Parameters ? FE[K] : never>) => ReturnType ? FE[K] : never>; // deprecated }; - export type FreezerNode> = T extends Array ? FreezerArray : T extends string | number | boolean | null | undefined ? T : FreezerObject; + export type FreezerNode = T extends Array ? FreezerArray : T extends string | number | boolean | null | undefined ? T : FreezerObject; - type FreezerCommon> = FreezerListener & { - getListener(): FreezerListener, + type FreezerCommon = FreezerListener & { + getListener(): FreezerListener, now(): void, pivot(): FreezerNodePivoted, reset(a: FreezerNode): FreezerNode, @@ -38,7 +38,7 @@ declare module 'freezer-js' { transact(): void, } - type FreezerArray> = FreezerCommon & { + type FreezerArray = FreezerCommon & { append(a: T[]): FreezerArray, pop(): T | undefined, prepend(): FreezerArray, @@ -58,7 +58,7 @@ declare module 'freezer-js' { : K }[keyof T], undefined> - type FreezerObject> = FreezerCommon & { + type FreezerObject = FreezerCommon & { remove(a: OptionalPropertyOf): FreezerObject, remove(a: OptionalPropertyOf[]): FreezerObject, } & { @@ -66,9 +66,9 @@ declare module 'freezer-js' { }; // Definitions repeated for pivots - export type FreezerNodePivoted, T> = T extends Array ? FreezerArrayPivoted : T extends string | number | boolean | null | undefined ? T : FreezerObjectPivoted; + export type FreezerNodePivoted = T extends Array ? FreezerArrayPivoted : T extends string | number | boolean | null | undefined ? T : FreezerObjectPivoted; - type FreezerCommonPivoted, T> = FreezerListener & { + type FreezerCommonPivoted = FreezerListener & { getListener(): void, now(): void, pivot(): FreezerNodePivoted, @@ -80,7 +80,7 @@ declare module 'freezer-js' { transact(): void, } - type FreezerArrayPivoted, T> = FreezerCommonPivoted & { + type FreezerArrayPivoted = FreezerCommonPivoted & { append(): FreezerNodePivoted, pop(): T | undefined, prepend(): FreezerNodePivoted, @@ -93,7 +93,7 @@ declare module 'freezer-js' { unshift(): FreezerNodePivoted, } & FreezerNodePivoted[]; - type FreezerObjectPivoted, T> = FreezerCommonPivoted & { + type FreezerObjectPivoted = FreezerCommonPivoted & { // remove(a: string): FreezerObject>, remove(a: string): FreezerNodePivoted, remove(a: string[]): FreezerNodePivoted, @@ -101,24 +101,24 @@ declare module 'freezer-js' { [K in keyof T]-?: FreezerNodePivoted }; - export class Freezer>{ + export class Freezer{ constructor(a: T, b?: FreezerOptions); - get() : FreezerNode>; - set(state: T) : FreezerNode>; - set(state: FreezerNode>) : FreezerNode>; + get() : FreezerNode; + set(state: T) : FreezerNode; + set(state: FreezerNode) : FreezerNode; // set() : FreezerCommon>; - getEventHub() : FreezerListener>; + getEventHub() : FreezerListener; - getData() : FreezerCommon>; - setData() : FreezerCommon>; + getData() : FreezerCommon; + setData() : FreezerCommon; - on: >(eventName: K, cb: FE[K]) => FreezerListener>; - once: >(eventName: K, cb: FE[K]) => FreezerListener>; - off: >(eventName: K, cb: FE[K]) => FreezerListener>; - emit: >(eventName: K, ...args: Parameters[K]>) => ReturnType[K]>; - trigger: >(eventName: K, ...args: Parameters[K]>) => ReturnType[K]>; // deprecated + on: >(eventName: K, cb: FE[K]) => FreezerListener; + once: >(eventName: K, cb: FE[K]) => FreezerListener; + off: >(eventName: K, cb: FE[K]) => FreezerListener; + emit: >(eventName: K, ...args: Parameters ? FE[K] : never>) => ReturnType ? FE[K] : never>; + trigger: >(eventName: K, ...args: Parameters ? FE[K] : never>) => ReturnType ? FE[K] : never>; // deprecated // reset(newData:any):void; diff --git a/src/state.ts b/src/state.ts index 509f4ab..71745c3 100644 --- a/src/state.ts +++ b/src/state.ts @@ -119,10 +119,10 @@ const defaultStateVars: StateVars = { valuesets: {}, } -const State = new Freezer>(defaultStateVars); +const State = new Freezer(defaultStateVars); // convenience -export type SageFreezerNode = FreezerNode>>; +export type SageFreezerNode = FreezerNode>; export type StateVarsFreezerNode = SageFreezerNode export type SageNodeInitializedFreezerNode = SageFreezerNode From 6ba2b3ca85979387bad9034612b4b031423a30bc Mon Sep 17 00:00:00 2001 From: Raghavan Chandrabalan Date: Tue, 25 Jan 2022 11:21:17 -0500 Subject: [PATCH 2/8] remove some anys --- src/domain-resource/value-editor.tsx | 2 +- src/freezer-js.d.ts | 2 ++ src/helpers/sage-utils.ts | 45 ++++++++++++++++------------ src/helpers/schema-utils.ts | 2 +- src/simplified/baseCard.tsx | 4 +-- src/simplified/simpleForm.tsx | 11 +++---- 6 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/domain-resource/value-editor.tsx b/src/domain-resource/value-editor.tsx index 930e4a8..90930d8 100644 --- a/src/domain-resource/value-editor.tsx +++ b/src/domain-resource/value-editor.tsx @@ -20,7 +20,7 @@ interface ValueEditorProps { onEditCancel: (e?: React.SyntheticEvent) => void, hasFocus: boolean, required: boolean, - shortName: any, + shortName: string, } class ValueEditor extends React.Component> { ESC_KEY: number; diff --git a/src/freezer-js.d.ts b/src/freezer-js.d.ts index fb5cf28..56e6515 100644 --- a/src/freezer-js.d.ts +++ b/src/freezer-js.d.ts @@ -5,6 +5,8 @@ declare module 'freezer-js' { freezeInstances: boolean, } + export type ExtractTypeOfFN = FN extends FreezerNode ? T : never; + export type EventDict = E extends {[K in keyof E]: (...args: any[]) => unknown} ? { [K in keyof E]: E[K] } : never; diff --git a/src/helpers/sage-utils.ts b/src/helpers/sage-utils.ts index e95400b..98f5bfa 100644 --- a/src/helpers/sage-utils.ts +++ b/src/helpers/sage-utils.ts @@ -1,33 +1,40 @@ import * as cql from 'cql-execution'; +import { Library } from 'fhir/r4'; -export function getCqlExecutionLibraryFromInputLibraryResource(resource: any) { - if (resource.content && Array.isArray(resource.content)) { - for (const content of resource.content) { - if (content.contentType == "application/elm+json") { - if (content.data) { - try { - const elm = JSON.parse(window.atob(content.data)); - const parsedLib = getCqlExecutionLibraryFromJsonElm(elm); - if (parsedLib) { - return { - library: parsedLib, - url: resource.url - }; +export function getCqlExecutionLibraryFromInputLibraryResource(resource: unknown) { + if (resource && typeof resource == 'object') { + const resourceToTest = resource as Partial; // Assume Partial type so that we're forced to check each property + if (!resourceToTest.url) { + return null; // Libraries are useless when building a PD if we can't reference them by URL + } + if (Array.isArray(resourceToTest.content)) { + for (const content of resourceToTest.content) { + if (content.contentType == "application/elm+json") { + if (content.data) { + try { + const elm = JSON.parse(window.atob(content.data)); + const parsedLib = getCqlExecutionLibraryFromJsonElm(elm); + if (parsedLib) { + return { + library: parsedLib, + url: resourceToTest.url + }; + } + } + catch(error) { + console.log("Could not parse encoded JSON ELM in resource:", resource); + return null; } - } - catch(error) { - console.log("Could not parse encoded JSON ELM in resource:", resource); - return null; } } } + console.log("No JSON ELM found in resource:", resource); } - console.log("No JSON ELM found in resource:", resource); } return null; } -export function getCqlExecutionLibraryFromJsonElm(elm: any) { +export function getCqlExecutionLibraryFromJsonElm(elm: unknown) { try { const parsedLib = new cql.Library(elm); console.log('parsed library into:', parsedLib); diff --git a/src/helpers/schema-utils.ts b/src/helpers/schema-utils.ts index 97f4d52..51ef820 100644 --- a/src/helpers/schema-utils.ts +++ b/src/helpers/schema-utils.ts @@ -27,7 +27,7 @@ export type SageNode = { type: ElementDefinitionType, level?: number, sliceName: string, - short: any, + short: string, isRequired: boolean, profile: string, // This profile must be able to resolve `schemaPath` binding?: any, diff --git a/src/simplified/baseCard.tsx b/src/simplified/baseCard.tsx index 1ea5acc..b52ae37 100644 --- a/src/simplified/baseCard.tsx +++ b/src/simplified/baseCard.tsx @@ -40,8 +40,8 @@ export const BaseCard = (props: BaseCardProps) => { classNames="res-card" > { - if (e.target.tagName !== "svg" && e.target.tagName !== "path" && props.clickable) { + onClick={(e: React.MouseEvent) => { + if (e.target instanceof Element && e.target.tagName !== "svg" && e.target.tagName !== "path" && props.clickable) { setShow(false); setTimeout(() => { if (State.get().bundle?.resources.length) { diff --git a/src/simplified/simpleForm.tsx b/src/simplified/simpleForm.tsx index 22135f9..1fdf3eb 100644 --- a/src/simplified/simpleForm.tsx +++ b/src/simplified/simpleForm.tsx @@ -1,6 +1,7 @@ import React, { FormEvent, useEffect, useState } from "react"; import { Form, Row , Col, InputGroup, DropdownButton, Dropdown, FormControl, Modal, Button} from 'react-bootstrap'; import State, { SageNodeInitializedFreezerNode } from "../state"; +import { ExtractTypeOfFN } from "freezer-js"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import {faCaretRight, faCaretLeft} from '@fortawesome/pro-solid-svg-icons'; import * as SchemaUtils from "../helpers/schema-utils" @@ -73,7 +74,7 @@ export const SimpleForm = (props:SimpleFormProps) => { () => { const initialLibraries = State.get().simplified.libraries; const librariesListener = initialLibraries.getListener(); - const updateCB = function(libraries: any, prevLibraries: any) { + const updateCB = function(libraries: ExtractTypeOfFN) { setLibraries(Object.keys(libraries).map((v) => { return { library: libraries[v].library, @@ -107,11 +108,11 @@ export const SimpleForm = (props:SimpleFormProps) => { const [showImportModal, setShowImportModal] = useState(false); const handleShowImportModal = () => setShowImportModal(true); const handleCloseImportModal = () => setShowImportModal(false); - const [FhirLibrary, setFhirLibrary] = useState(); + const [FhirLibraryStr, setFhirLibraryStr] = useState(); const handleImportLibrary = () => { - if (FhirLibrary) { + if (FhirLibraryStr) { try { - const parsedFhir = JSON.parse(FhirLibrary); + const parsedFhir = JSON.parse(FhirLibraryStr); const newLib = SageUtils.getCqlExecutionLibraryFromInputLibraryResource(parsedFhir); if (newLib) { State.emit("load_library", newLib.library, newLib.url, parsedFhir); @@ -137,7 +138,7 @@ export const SimpleForm = (props:SimpleFormProps) => { setFhirLibrary(e.currentTarget.value)} + onChange={(e) => setFhirLibraryStr(e.currentTarget.value)} /> From 75adff703896f8795d105a5a244e5456253d40f2 Mon Sep 17 00:00:00 2001 From: Raghavan Chandrabalan Date: Wed, 26 Jan 2022 15:30:08 -0500 Subject: [PATCH 3/8] change formatting --- src/simplified/simpleForm.tsx | 317 +++++++++++++++++----------------- 1 file changed, 160 insertions(+), 157 deletions(-) diff --git a/src/simplified/simpleForm.tsx b/src/simplified/simpleForm.tsx index 0270904..2d48b4f 100644 --- a/src/simplified/simpleForm.tsx +++ b/src/simplified/simpleForm.tsx @@ -29,7 +29,7 @@ interface SimpleFormProps { } const buildConditionFromSelection = (expression?: string): PlanDefinitionActionCondition => { - var insertedExpression = ""; + let insertedExpression = ""; if (expression) { insertedExpression = expression; } @@ -91,180 +91,183 @@ export const SimpleForm = (props:SimpleFormProps) => { } }, [] - ) + ); - const [libraries, setLibraries] = useState<{library: cql.Library, url: string}[]>([]); - useEffect( - () => { - setExpressionOptions(getExpressionOptionsFromLibraries(libraries)); - return; - }, - [libraries], - ); - - // Import Library Modal - // Allows the user to import a new CQL Library to use as a condition - const [showImportModal, setShowImportModal] = useState(false); - const handleShowImportModal = () => setShowImportModal(true); - const handleCloseImportModal = () => setShowImportModal(false); - const [FhirLibrary, setFhirLibrary] = useState(); - const handleImportLibrary = () => { - if (FhirLibrary) { - try { - const parsedFhir = JSON.parse(FhirLibrary); - const newLib = SageUtils.getCqlExecutionLibraryFromInputLibraryResource(parsedFhir); - if (newLib) { - State.emit("load_library", newLib.library, newLib.url, parsedFhir); - } - } - catch (err) { - console.log("Could not parse FHIR Library as JSON object"); - return; - } + const [libraries, setLibraries] = useState<{library: cql.Library, url: string}[]>([]); + useEffect( + () => { + setExpressionOptions(getExpressionOptionsFromLibraries(libraries)); + return; + }, + [libraries], + ); + + // Import Library Modal + // Allows the user to import a new CQL Library to use as a condition + const [showImportModal, setShowImportModal] = useState(false); + const handleShowImportModal = () => setShowImportModal(true); + const handleCloseImportModal = () => setShowImportModal(false); + const [FhirLibrary, setFhirLibrary] = useState(); + const handleImportLibrary = () => { + if (FhirLibrary) { + try { + const parsedFhir = JSON.parse(FhirLibrary); + const newLib = SageUtils.getCqlExecutionLibraryFromInputLibraryResource(parsedFhir); + if (newLib) { + State.emit("load_library", newLib.library, newLib.url, parsedFhir); } - }; - const importModalElement = (<> - - + } + catch (err) { + console.log("Could not parse FHIR Library as JSON object"); + return; + } + } + }; + + const importModalElement = (<> + + Import Library - - + + Paste in the FHIR Library Resource that contains the condition you wish to use below:
- - setFhirLibrary(e.currentTarget.value)} - /> - + + setFhirLibrary(e.currentTarget.value)} + /> +
Please note that any dependencies of the pasted FHIR Library will not be automatically resolved or added to the final Bundle. -
- + + - -
- ); - - // All logic for saving the Simplified Form data into the underlying FHIR Resources should be performed here - const handleSaveResource = function() { - if (props.actNode.displayName == ACTIVITY_DEFINITION) { // Questionnaires have trouble saving otherwise - State.emit("value_change", SchemaUtils.getChildOfNode(props.actNode, "title"), title, false); - State.emit("value_change", SchemaUtils.getChildOfNode(props.actNode, "description"), description, false); - State.emit("value_change", SchemaUtils.getChildOfNode(props.actNode, "experimental"), State.get().experimental, false); - State.emit("value_change", SchemaUtils.getChildOfNode(props.actNode, "status"), State.get().status, false); - } - State.emit("value_change", SchemaUtils.getChildOfNode(props.planNode, "title"), title, false); - State.emit("value_change", SchemaUtils.getChildOfNode(props.planNode, "description"), description, false); - State.emit("value_change", SchemaUtils.getChildOfNode(props.planNode, "experimental"), State.get().experimental, false); - State.emit("value_change", SchemaUtils.getChildOfNode(props.planNode, "status"), State.get().status, false); - const titleNode = SchemaUtils.getChildOfNodePath(props.planNode, ["action", "title"]); - if (titleNode) { - State.emit("value_change", titleNode, title, false); - } - const descriptionNode = SchemaUtils.getChildOfNodePath(props.planNode, ["action", "description"]); - if (descriptionNode) { - State.emit("value_change", descriptionNode, description, false); - } - const conditionNode = SchemaUtils.getChildOfNodePath(props.planNode, ["action", "condition"]); - if (conditionNode) { - const conditionNodes = SchemaUtils.getChildrenFromObjectArrayNode(conditionNode); - State.emit("load_json_into", conditionNodes[0], condition); - State.emit("value_change", SchemaUtils.getChildOfNode(props.planNode, "library"), [selectedLibrary], false); - } - State.get().set("ui", {status:"collection"}) - } - - return ( -
- - {importModalElement} -
- - -

- {props.actNode ? profileToFriendlyResourceListEntry(SchemaUtils.toFhir(props.actNode, false).meta?.profile?.[0])?.FRIENDLY ?? "Unknown Resource Type" : ""} -

- - - Title - + + + ); + + // All logic for saving the Simplified Form data into the underlying FHIR Resources should be performed here + const handleSaveResource = function() { + if (props.actNode.displayName == ACTIVITY_DEFINITION) { // Questionnaires have trouble saving otherwise + State.emit("value_change", SchemaUtils.getChildOfNode(props.actNode, "title"), title, false); + State.emit("value_change", SchemaUtils.getChildOfNode(props.actNode, "description"), description, false); + State.emit("value_change", SchemaUtils.getChildOfNode(props.actNode, "experimental"), State.get().experimental, false); + State.emit("value_change", SchemaUtils.getChildOfNode(props.actNode, "status"), State.get().status, false); + } + State.emit("value_change", SchemaUtils.getChildOfNode(props.planNode, "title"), title, false); + State.emit("value_change", SchemaUtils.getChildOfNode(props.planNode, "description"), description, false); + State.emit("value_change", SchemaUtils.getChildOfNode(props.planNode, "experimental"), State.get().experimental, false); + State.emit("value_change", SchemaUtils.getChildOfNode(props.planNode, "status"), State.get().status, false); + const titleNode = SchemaUtils.getChildOfNodePath(props.planNode, ["action", "title"]); + if (titleNode) { + State.emit("value_change", titleNode, title, false); + } + const descriptionNode = SchemaUtils.getChildOfNodePath(props.planNode, ["action", "description"]); + if (descriptionNode) { + State.emit("value_change", descriptionNode, description, false); + } + const conditionNode = SchemaUtils.getChildOfNodePath(props.planNode, ["action", "condition"]); + if (conditionNode) { + const conditionNodes = SchemaUtils.getChildrenFromObjectArrayNode(conditionNode); + State.emit("load_json_into", conditionNodes[0], condition); + State.emit("value_change", SchemaUtils.getChildOfNode(props.planNode, "library"), [selectedLibrary], false); + } + State.get().set("ui", {status:"collection"}) + } + + return ( +
+ {importModalElement} + + + +

+ {props.actNode ? profileToFriendlyResourceListEntry(SchemaUtils.toFhir(props.actNode, false).meta?.profile?.[0])?.FRIENDLY ?? "Unknown Resource Type" : ""} +

+ + + Title + setTitle(e.currentTarget.value)} - /> - - - - - Description - + + + + + Description + setDescription(e.currentTarget.value)} - /> - - - - - Condition - + /> + + + + + Condition + { - if (e.currentTarget.value == '') { - setCondition(buildConditionFromSelection()); - setSelectedLibrary(""); - } - else if (e.currentTarget.value == '[[import library]]') { - handleShowImportModal(); - setCondition(buildConditionFromSelection()); - setSelectedLibrary(""); - } - else { - setCondition(buildConditionFromSelection(e.currentTarget.value)); - setSelectedLibrary(expressionOptions[e.currentTarget.value].libraryUrl); - } - }} - value={condition.expression?.expression} + onChange={(e) => { + if (e.currentTarget.value == '') { + setCondition(buildConditionFromSelection()); + setSelectedLibrary(""); + } + else if (e.currentTarget.value == '[[import library]]') { + handleShowImportModal(); + setCondition(buildConditionFromSelection()); + setSelectedLibrary(""); + } + else { + setCondition(buildConditionFromSelection(e.currentTarget.value)); + setSelectedLibrary(expressionOptions[e.currentTarget.value].libraryUrl); + } + }} + value={condition.expression?.expression} > - - {Object.keys(expressionOptions).map((v) => { - const exprOption = expressionOptions[v]; - return - })} - + + {Object.keys(expressionOptions).map((v) => { + const exprOption = expressionOptions[v]; + return + })} + - - - - -
); - - } - \ No newline at end of file + +
+
+ +
+ ); + +} + \ No newline at end of file From e0d43b37ad1ebb244d6904bb5ad0eba6495e49ec Mon Sep 17 00:00:00 2001 From: Raghavan Chandrabalan Date: Wed, 26 Jan 2022 15:43:18 -0500 Subject: [PATCH 4/8] allow last resource to be deleted from basic view --- src/simplified/baseCard.tsx | 1 - src/simplified/collection.tsx | 3 ++- src/simplified/folder.tsx | 2 +- src/simplified/simpleForm.tsx | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/simplified/baseCard.tsx b/src/simplified/baseCard.tsx index ca7e07d..2ecc276 100644 --- a/src/simplified/baseCard.tsx +++ b/src/simplified/baseCard.tsx @@ -36,7 +36,6 @@ export const BaseCard = (props: BaseCardProps) => { let headerPadding = {}; if (props.title == "") headerPadding = {padding:"7px"}; const resourceType = friendlyToFhir(props.header); - const isActivity = resourceType?.includes("Activity"); return ( { /> }) - } + } + { resources.length == 0 ?
No Cards
: undefined } ); diff --git a/src/simplified/folder.tsx b/src/simplified/folder.tsx index 6cf4982..0eaf606 100644 --- a/src/simplified/folder.tsx +++ b/src/simplified/folder.tsx @@ -56,7 +56,7 @@ export const Folder = (props: FolderProps) => { }/> - {State.get().bundle.resources.length > 2 && + {
{ diff --git a/src/simplified/simpleForm.tsx b/src/simplified/simpleForm.tsx index 2d48b4f..ef5de86 100644 --- a/src/simplified/simpleForm.tsx +++ b/src/simplified/simpleForm.tsx @@ -194,8 +194,7 @@ export const SimpleForm = (props:SimpleFormProps) => {
{importModalElement}
-

diff --git a/test/basic-cpg.test.jsx b/test/basic-cpg.test.jsx index 984ae5e..2d48205 100644 --- a/test/basic-cpg.test.jsx +++ b/test/basic-cpg.test.jsx @@ -43,16 +43,16 @@ test('Create a basic CPG with a single PD that uses the hypertension library and // CpgDialog open on screen userEvent.click(await screen.findByRole('button', {name: 'Open Resource'})); // SelectView - userEvent.click(await screen.findByText('AdministerMedication')); + userEvent.click(await screen.findByText('Give Medication')); // SimpleForm - await screen.findByText('ActivityDefinition/Plandefinition'); + await screen.findByText('Save Card'); userEvent.type(screen.getByLabelText('Title'), '123'); userEvent.type(screen.getByLabelText('Description'), '321'); userEvent.selectOptions(screen.getByLabelText('Condition'), 'HypertensionCA.Mean BP >= 180/110'); - fireEvent.submit(screen.getByRole('button', {name: 'Save Resource'})); + fireEvent.submit(screen.getByRole('button', {name: 'Save Card'})); // Collection - await screen.findByText('Saved Resources'); - userEvent.click(screen.getByRole('button', {name: 'Export Resource'})); + await screen.findByText('Saved Cards'); + userEvent.click(screen.getByRole('button', {name: 'Export as FHIR Bundle'})); // ExportDialog open on screen await screen.findByText('Exported FHIR JSON'); expect(JSON.parse(screen.getByRole('textbox', {name: "exportedJson"}).textContent)).toStrictEqual(basicCpgExport); From 25ae4ef6fe360988c60ac2f10272f6b9c12f7df3 Mon Sep 17 00:00:00 2001 From: Raghavan Chandrabalan Date: Wed, 26 Jan 2022 15:59:14 -0500 Subject: [PATCH 6/8] increase jest timeout for ci --- jest.setup.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jest.setup.js b/jest.setup.js index 4fd5e98..1c149a8 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -4,6 +4,9 @@ import $ from 'jquery'; global.$ = $; jest.setTimeout(10000); +if (process.env.CI) { + jest.setTimeout(30000); // Initial loading of profiles takes a lot longer on GH Actions +} // Mock functions window.scrollTo = jest.fn(); // unimplemented in jsdom From a0ba09db5892233521ef7ee1db544d3c8ee6aa29 Mon Sep 17 00:00:00 2001 From: Raghavan Chandrabalan Date: Wed, 26 Jan 2022 16:07:54 -0500 Subject: [PATCH 7/8] increase timeout for initial load --- .eslintrc.cjs | 3 ++- test/basic-cpg.test.jsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 0456a61..fd39f3b 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -61,7 +61,8 @@ module.exports = { "**/*.test.jsx" ], env: { - jest: true + "jest": true, + "node": true }, "extends": [ "eslint:recommended", diff --git a/test/basic-cpg.test.jsx b/test/basic-cpg.test.jsx index 2d48205..1652409 100644 --- a/test/basic-cpg.test.jsx +++ b/test/basic-cpg.test.jsx @@ -37,7 +37,7 @@ afterAll(() => server.close()); test('Create a basic CPG with a single PD that uses the hypertension library and export it', async () => { render(); // Wait for profiles to load - await waitForElementToBeRemoved(() => screen.queryByRole('progressbar', {name: "loading-symbol"}), {timeout: 5000, interval: 1000}); + await waitForElementToBeRemoved(() => screen.queryByRole('progressbar', {name: "loading-symbol"}), {timeout: process.env.CI ? 30000 : 5000, interval: 1000}); // RootComponent userEvent.click(screen.getAllByRole('button', {name: 'Basic CPG'})[0]); // CpgDialog open on screen From 930e6c720e7c0f463a320897a14a5b951fccbcbe Mon Sep 17 00:00:00 2001 From: Josh Liben Date: Fri, 28 Jan 2022 10:42:25 -0500 Subject: [PATCH 8/8] refactor folder rendering --- src/simplified/collection.tsx | 76 +++++++++++++++++++---------------- src/simplified/folder.tsx | 5 ++- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/simplified/collection.tsx b/src/simplified/collection.tsx index 356181c..fd01f56 100644 --- a/src/simplified/collection.tsx +++ b/src/simplified/collection.tsx @@ -1,10 +1,11 @@ -import {useState, useEffect} from "react"; -import {Folder} from"./folder"; -import State from "../state"; +import { useState, useEffect } from "react"; +import { Folder } from "./folder"; +import State, { SageReactions } from "../state"; import * as SchemaUtils from "../helpers/schema-utils" import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import {faDownload, faCaretLeft} from '@fortawesome/pro-solid-svg-icons'; +import { faDownload, faCaretLeft } from '@fortawesome/pro-solid-svg-icons'; +import { SageNodeInitialized } from "../helpers/schema-utils"; const Collection = () => { @@ -12,42 +13,49 @@ const Collection = () => { const resources = State.get().bundle?.resources ?? []; return ( -
+
-

Saved Cards

- - +

Saved Cards

+ +
{ - resources.map( - (resource, i) => { - if (i % 2 == 1) return; // Every other resource would be a plandef - const actTitleNode = SchemaUtils.getChildOfNode(resource, "title"); - const planTitleNode = SchemaUtils.getChildOfNode(resources[i+1], "title"); - const firstExpression: string | undefined = SchemaUtils.getChildOfNodePath(resources[i+1], ["action", "condition", "expression", "expression"])?.value; + resources.reduce( + function (accumulator: any[], currentValue, currentIndex, array) { + if (currentIndex % 2 === 0) + accumulator.push(array.slice(currentIndex, currentIndex + 2)); + return accumulator; + }, []).map((pair, i) => { + const actNode = pair[0]; + const planDefNode = pair[1]; + const actTitleNode = SchemaUtils.getChildOfNode(actNode, "title"); + const planTitleNode = SchemaUtils.getChildOfNode(planDefNode, "title"); + const actDescNode = SchemaUtils.getChildOfNode(actNode, "description"); + const firstExpression: string | undefined = SchemaUtils.getChildOfNodePath(planDefNode, ["action", "condition", "expression", "expression"])?.value; const conditionExpressions: string[] = firstExpression ? [firstExpression] : []; - return
- -
- }) + return
+ +
+ }) } - { resources.length == 0 ?
No Cards
: undefined } + {resources.length == 0 ?
No Cards
: undefined}
); diff --git a/src/simplified/folder.tsx b/src/simplified/folder.tsx index 0eaf606..f4139ae 100644 --- a/src/simplified/folder.tsx +++ b/src/simplified/folder.tsx @@ -3,10 +3,11 @@ import {BaseCard} from"./baseCard"; import { CSSTransition } from 'react-transition-group'; import State from "../state"; import { CloseButton } from "react-bootstrap"; -import { ACTIVITY_DEFINITION, getBorderPropsForType, PLAN_DEFINITION, profileToFriendlyResourceListEntry, profileToFriendlyResourceSelf } from "./nameHelpers"; +import {getBorderPropsForType, PLAN_DEFINITION, profileToFriendlyResourceListEntry, profileToFriendlyResourceSelf } from "./nameHelpers"; interface FolderProps { actTitle: string, + actDesc: string, planTitle: string, conditionExpressions: string[], index: number, @@ -52,7 +53,7 @@ export const Folder = (props: FolderProps) => { - {props.actTitle} {props.conditionExpressions.length > 0 ? `WHEN ${props.conditionExpressions[0]} IS TRUE` : ""} + {props.actDesc} {props.conditionExpressions.length > 0 ? `WHEN ${props.conditionExpressions[0]} IS TRUE` : ""} }/>