diff --git a/ImageCrop/ImageCrop/imageCropApp.tsx b/ImageCrop/ImageCrop/imageCropApp.tsx index adf4c82..35e8ec4 100644 --- a/ImageCrop/ImageCrop/imageCropApp.tsx +++ b/ImageCrop/ImageCrop/imageCropApp.tsx @@ -1,6 +1,6 @@ import * as React from "react"; -import { PcfContextProvider } from "./services/pcfContext"; -import { PcfContextService } from "./services/pcfContextService"; +import { PcfContextProvider } from "pcf-context-service"; +import { PcfContextService } from "pcf-context-service"; import ImageCropControl from "./components/imageCropControl"; import { IImageCropControlProps } from "./types/imageCropTypes"; diff --git a/ImageCrop/ImageCrop/services/pcfContext.tsx b/ImageCrop/ImageCrop/services/pcfContext.tsx index 30a6379..d2b4680 100644 --- a/ImageCrop/ImageCrop/services/pcfContext.tsx +++ b/ImageCrop/ImageCrop/services/pcfContext.tsx @@ -1,6 +1,6 @@ import * as React from "react"; -import { PcfContextService } from './pcfContextService' +import { PcfContextService } from 'pcf-context-service' // Provides a React context for sharing the PCF context service across the component tree. // Props for the PcfContextProvider component diff --git a/ImageCrop/package.json b/ImageCrop/package.json index e28b1f6..4002d4f 100644 --- a/ImageCrop/package.json +++ b/ImageCrop/package.json @@ -16,7 +16,8 @@ "@types/react-dom": "^19.1.6", "react-dom": "^19.1.0", "react-image-crop": "^11.0.10", - "uuid": "^11.1.0" + "uuid": "^11.1.0", + "pcf-context-service": "file:../packages/pcf-context-service" }, "devDependencies": { "@eslint/js": "^9.25.1", diff --git a/ImageCrop/tsconfig.json b/ImageCrop/tsconfig.json index 34a4720..865f54c 100644 --- a/ImageCrop/tsconfig.json +++ b/ImageCrop/tsconfig.json @@ -1,6 +1,10 @@ { "extends": "./node_modules/pcf-scripts/tsconfig_base.json", "compilerOptions": { - "typeRoots": ["node_modules/@types"] + "typeRoots": ["node_modules/@types"], + "baseUrl": "./ImageCrop", + "paths": { + "pcf-context-service": ["../packages/pcf-context-service/src"] + } } } diff --git a/Scheduler/Scheduler/hooks/useAvailableViews.ts b/Scheduler/Scheduler/hooks/useAvailableViews.ts index bab70ad..998e936 100644 --- a/Scheduler/Scheduler/hooks/useAvailableViews.ts +++ b/Scheduler/Scheduler/hooks/useAvailableViews.ts @@ -3,7 +3,7 @@ import { SCHEDULER_VIEWS } from "../types/schedulerViews"; import { getLocalizedViewName } from "../utils/localization"; import { DEFAULT_VIEW_NAMES } from "../utils/constants"; import { SchedulerData } from "react-big-schedule"; -import { PcfContextService } from "../services/pcfContextService"; +import { PcfContextService } from "pcf-context-service"; import { SchedulerAction } from "../types"; export function useAvailableViews( diff --git a/Scheduler/Scheduler/hooks/useDayViewOptions.ts b/Scheduler/Scheduler/hooks/useDayViewOptions.ts index 15b5202..d7c88b2 100644 --- a/Scheduler/Scheduler/hooks/useDayViewOptions.ts +++ b/Scheduler/Scheduler/hooks/useDayViewOptions.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import { SchedulerData } from "react-big-schedule"; -import { PcfContextService } from "../services/pcfContextService"; +import { PcfContextService } from "pcf-context-service"; import { SchedulerAction } from "../types"; export interface DayViewOptions { diff --git a/Scheduler/Scheduler/hooks/useDisplayWeekend.ts b/Scheduler/Scheduler/hooks/useDisplayWeekend.ts index ba581b1..789a37f 100644 --- a/Scheduler/Scheduler/hooks/useDisplayWeekend.ts +++ b/Scheduler/Scheduler/hooks/useDisplayWeekend.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import { SchedulerData } from "react-big-schedule"; -import { PcfContextService } from "../services/pcfContextService"; +import { PcfContextService } from "pcf-context-service"; import { SchedulerAction } from "../types"; /** diff --git a/Scheduler/Scheduler/hooks/useNonWorkingTimeColors.ts b/Scheduler/Scheduler/hooks/useNonWorkingTimeColors.ts index 6f3a605..8b1b4eb 100644 --- a/Scheduler/Scheduler/hooks/useNonWorkingTimeColors.ts +++ b/Scheduler/Scheduler/hooks/useNonWorkingTimeColors.ts @@ -1,7 +1,7 @@ import { secureHeapUsed } from "crypto"; import { useEffect, useState } from "react"; import { SchedulerData } from "react-big-schedule"; -import { PcfContextService } from "../services/pcfContextService"; +import { PcfContextService } from "pcf-context-service"; import { SchedulerAction } from "../types"; export interface NonWorkingTimeColors { diff --git a/Scheduler/Scheduler/hooks/useResourceNameHeader.ts b/Scheduler/Scheduler/hooks/useResourceNameHeader.ts index d7da93e..49b8ceb 100644 --- a/Scheduler/Scheduler/hooks/useResourceNameHeader.ts +++ b/Scheduler/Scheduler/hooks/useResourceNameHeader.ts @@ -1,7 +1,7 @@ import { useState, useEffect } from "react"; import { SchedulerData } from "react-big-schedule"; import { getLocalizedResourceName } from "../utils/localization"; -import { PcfContextService } from "../services/pcfContextService"; +import { PcfContextService } from "pcf-context-service"; import { SchedulerAction } from "../types"; export function useResourceNameHeader( diff --git a/Scheduler/Scheduler/hooks/useSchedulerDate.ts b/Scheduler/Scheduler/hooks/useSchedulerDate.ts index 50fe232..4beffef 100644 --- a/Scheduler/Scheduler/hooks/useSchedulerDate.ts +++ b/Scheduler/Scheduler/hooks/useSchedulerDate.ts @@ -2,7 +2,7 @@ import { useState, useEffect } from "react"; import { SchedulerData } from "react-big-schedule"; import { Event, SchedulerAction } from "../types/schedulerTypes"; import { parseDateOnly } from "../utils/formattingHelpers"; -import { PcfContextService } from "../services/pcfContextService"; +import { PcfContextService } from "pcf-context-service"; export function useSchedulerDate( pcfContext: PcfContextService, diff --git a/Scheduler/Scheduler/hooks/useSchedulerView.ts b/Scheduler/Scheduler/hooks/useSchedulerView.ts index 8dc7c0a..8abc497 100644 --- a/Scheduler/Scheduler/hooks/useSchedulerView.ts +++ b/Scheduler/Scheduler/hooks/useSchedulerView.ts @@ -2,7 +2,7 @@ import { useState, useEffect } from "react"; import { getViewByName, View } from "../types/schedulerViews"; import { SchedulerData } from "react-big-schedule"; import { Event, SchedulerAction } from "../types/schedulerTypes"; -import { PcfContextService } from "../services/pcfContextService"; +import { PcfContextService } from "pcf-context-service"; export function useSchedulerView( pcfContext: PcfContextService, diff --git a/Scheduler/Scheduler/hooks/useShowHeader.ts b/Scheduler/Scheduler/hooks/useShowHeader.ts index 70e6b37..ec2497e 100644 --- a/Scheduler/Scheduler/hooks/useShowHeader.ts +++ b/Scheduler/Scheduler/hooks/useShowHeader.ts @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; import { SchedulerData } from "react-big-schedule"; -import { PcfContextService } from "../services/pcfContextService"; +import { PcfContextService } from "pcf-context-service"; import { SchedulerAction } from "../types"; export function useShowHeader( diff --git a/Scheduler/Scheduler/hooks/useWorkWeekDays.ts b/Scheduler/Scheduler/hooks/useWorkWeekDays.ts index 6ad841e..4116205 100644 --- a/Scheduler/Scheduler/hooks/useWorkWeekDays.ts +++ b/Scheduler/Scheduler/hooks/useWorkWeekDays.ts @@ -1,7 +1,7 @@ import { useState, useEffect } from "react"; import { SchedulerData } from "react-big-schedule"; import { Event, SchedulerAction } from "../types/schedulerTypes"; -import { PcfContextService } from "../services/pcfContextService"; +import { PcfContextService } from "pcf-context-service"; /** * Returns an array of day numbers representing the work week range, diff --git a/Scheduler/Scheduler/schedulerApp.tsx b/Scheduler/Scheduler/schedulerApp.tsx index a6f77bb..debf9d4 100644 --- a/Scheduler/Scheduler/schedulerApp.tsx +++ b/Scheduler/Scheduler/schedulerApp.tsx @@ -1,6 +1,6 @@ import * as React from "react"; -import { PcfContextProvider } from "./services/pcfContext"; -import { PcfContextService } from "./services/pcfContextService"; +import { PcfContextProvider } from "pcf-context-service"; +import { PcfContextService } from "pcf-context-service"; import SchedulerControl from "./components/scheduler"; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; diff --git a/Scheduler/Scheduler/services/pcfContext.tsx b/Scheduler/Scheduler/services/pcfContext.tsx index 2eb57a2..858c1c9 100644 --- a/Scheduler/Scheduler/services/pcfContext.tsx +++ b/Scheduler/Scheduler/services/pcfContext.tsx @@ -1,6 +1,6 @@ import * as React from "react"; -import { PcfContextService } from './pcfContextService' +import { PcfContextService } from 'pcf-context-service' // Provides a React context for sharing the PCF context service across the component tree. // Props for the PcfContextProvider component diff --git a/Scheduler/Scheduler/test/testApp.tsx b/Scheduler/Scheduler/test/testApp.tsx index 6f227f9..3c7606c 100644 --- a/Scheduler/Scheduler/test/testApp.tsx +++ b/Scheduler/Scheduler/test/testApp.tsx @@ -2,12 +2,12 @@ import * as React from "react"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; import SchedulerControl from "../components/scheduler"; -import { PcfContextProvider } from "../services/pcfContext"; -import { PcfContextService } from "../services/pcfContextService"; -import { MockPCFContext } from "../mocks/MockPCFContext"; -import { MockPCFParameters } from "../mocks/MockPCFParameters"; -import { MockPCFMode } from "../mocks/MockPCFMode"; -import { MockPCFDataSet } from "../mocks/MockPCFDataSet"; +import { PcfContextProvider } from "pcf-context-service"; +import { PcfContextService } from "pcf-context-service"; +import { MockPCFContext } from "pcf-mocks"; +import { MockPCFParameters } from "pcf-mocks"; +import { MockPCFMode } from "pcf-mocks"; +import { MockPCFDataSet } from "pcf-mocks"; import { ISchedulerControlProps } from "../types"; // Use the new mock context for testing diff --git a/Scheduler/package.json b/Scheduler/package.json index 8fd3c62..f6e0247 100644 --- a/Scheduler/package.json +++ b/Scheduler/package.json @@ -24,7 +24,9 @@ "react-dnd": "^14.0.5", "react-dnd-html5-backend": "^14.0.5", "react-dom": "^18.3.1", - "uuid": "^11.1.0" + "uuid": "^11.1.0", + "pcf-context-service": "file:../packages/pcf-context-service", + "pcf-mocks": "file:../packages/pcf-mocks" }, "devDependencies": { "@eslint/js": "^9.17.0", diff --git a/Scheduler/tsconfig.json b/Scheduler/tsconfig.json index b84fa32..0b945dc 100644 --- a/Scheduler/tsconfig.json +++ b/Scheduler/tsconfig.json @@ -3,10 +3,15 @@ "compilerOptions": { "typeRoots": [ "node_modules/@types" - ], + ], "allowSyntheticDefaultImports": true, "esModuleInterop": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + "baseUrl": "./Scheduler", + "paths": { + "pcf-context-service": ["../../packages/pcf-context-service/src"], + "pcf-mocks": ["../../packages/pcf-mocks/src"] + } }, "exclude": ["./out"] } \ No newline at end of file diff --git a/packages/pcf-context-service/package.json b/packages/pcf-context-service/package.json new file mode 100644 index 0000000..fa1fad7 --- /dev/null +++ b/packages/pcf-context-service/package.json @@ -0,0 +1,6 @@ +{ + "name": "pcf-context-service", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts" +} diff --git a/packages/pcf-context-service/src/index.ts b/packages/pcf-context-service/src/index.ts new file mode 100644 index 0000000..c2d0829 --- /dev/null +++ b/packages/pcf-context-service/src/index.ts @@ -0,0 +1,2 @@ +export * from './pcfContextService'; +export * from './pcfContext'; diff --git a/packages/pcf-context-service/src/pcfContext.tsx b/packages/pcf-context-service/src/pcfContext.tsx new file mode 100644 index 0000000..2eb57a2 --- /dev/null +++ b/packages/pcf-context-service/src/pcfContext.tsx @@ -0,0 +1,29 @@ + +import * as React from "react"; +import { PcfContextService } from './pcfContextService' +// Provides a React context for sharing the PCF context service across the component tree. + +// Props for the PcfContextProvider component +interface PcfContextProviderProps { + pcfcontext: PcfContextService; + children: React.ReactNode; +} + + +// Create a React context for the PCF context service +const PcfContext = React.createContext(undefined!) + +// Provider component for the PCF context service +export const PcfContextProvider = ({ pcfcontext, children }: PcfContextProviderProps) => { + return ( + + {children} + + ) +} + + +// Custom hook to access the PCF context service +export const usePcfContext = () => { + return React.useContext(PcfContext); +} diff --git a/packages/pcf-context-service/src/pcfContextService.ts b/packages/pcf-context-service/src/pcfContextService.ts new file mode 100644 index 0000000..b4a26f6 --- /dev/null +++ b/packages/pcf-context-service/src/pcfContextService.ts @@ -0,0 +1,176 @@ +//import { Theme } from "@fluentui/react-components"; +import { IInputs } from "../generated/ManifestTypes"; + + +// Props for constructing a PcfContextService instance +export interface IPcfContextServiceProps { + context: ComponentFramework.Context; + instanceid: string; + height: number; +} + + +// Maximum width for small form factor (e.g., phone) +const SmallFormFactorMaxWidth = 350; + + +// Enum for supported form factors +const enum FormFactors { + Unknown = 0, + Desktop = 1, + Tablet = 2, + Phone = 3, +} + + +// Interface for context info (entity type and ID) +interface ContextInfo { + entityTypeName: string; + entityId: string; +} + + +// Service for accessing and managing PCF context and environment details +export class PcfContextService { + instanceid: string; + context: ComponentFramework.Context; + //theme: Theme; + formFactor: string; + height: number; + + /** + * Construct a new PcfContextService + * @param props - context, instanceid, and height for the control + */ + constructor(props: IPcfContextServiceProps) { + this.instanceid = props.instanceid; + this.context = props.context; + //this.theme = this.getTheme(); + this.formFactor = + props.context.client.getFormFactor() == (FormFactors.Phone as number) || + props.context.mode.allocatedWidth < SmallFormFactorMaxWidth + ? "small" + : "large"; + this.height = props.height; + } + + + /** + * Update the context reference (e.g., on re-render) + */ + updateContext(context: ComponentFramework.Context) { + this.context = context; + } + + /** + * Returns true if the control is running in the Power Apps designer + */ + public inDesignMode(): boolean { + // Previously only handled commercial cloud. + // Updated to also handle GCC, GCC High, and DoD maker portal URLs. + const designModeUrls = [ + "make.powerapps.com", + "make.gov.powerapps.us", // GCC + "make.high.powerapps.us", // GCC High + "make.apps.appsplatform.us", // DoD + ]; + const currentUrl = window.location.href; + return designModeUrls.some((url) => currentUrl.includes(url)); + } + + /** + * Returns true if the control is running in a Canvas app + */ + public isCanvasApp(): boolean { + return this.context.mode.allocatedHeight !== -1; + } + + /** + * Returns true if the control is disabled + */ + public isControlDisabled(): boolean { + // Return the control's disabled state from the context + return this.context.mode.isControlDisabled; + } + + /** + * Returns true if the control is visible + */ + public isVisible(): boolean { + return this.context.mode.isVisible; + } + + // public getTheme(): Theme { + // const defaultTheme: Theme = this.context.fluentDesignLanguage + // ?.tokenTheme as Theme; + // return this.isControlDisabled() && !this.isCanvasApp() + // ? { + // ...defaultTheme, + // colorCompoundBrandStroke: defaultTheme?.colorNeutralStroke1, + // colorCompoundBrandStrokeHover: defaultTheme?.colorNeutralStroke1Hover, + // colorCompoundBrandStrokePressed: + // defaultTheme?.colorNeutralStroke1Pressed, + // } + // : defaultTheme; + // } + + /** + * Returns the entity type name from the context (model-driven only) + */ + public getEntityTypeName(): string { + // @ts-expect-error Assert contextInfo to a known type. + const contextInfo = this.context.mode.contextInfo as ContextInfo; + return contextInfo.entityTypeName; + } + + /** + * Returns the entity ID from the context (model-driven only) + */ + public getEntityId(): string { + // @ts-expect-error Assert contextInfo to a known type. + const contextInfo = this.context.mode.contextInfo as ContextInfo; + return contextInfo.entityId; + } + + // Returns the base URL for the current context in Model-driven apps only. + // In Canvas apps, this will be undefined. + /** + * Returns the base URL for the current context (model-driven only) + */ + public getBaseUrl(): string | undefined { + if (this.isCanvasApp()) { + return undefined; + } else { + // @ts-expect-error context is available in model apps + return this.context.page.getClientUrl(); + } + } + + /** + * Returns a localized string from the context resources + */ + public getResourceString(key: string): string { + return this.context.resources.getString(key) || key; + } + + // If the pcf is in full page mode such as when it's being loaded through the sitemap + // this will return any parameters that were passed in the URL. + /** + * Returns a parameter from the full page URL (model-driven only) + */ + public getFullPageParam(key: string): string { + // @ts-expect-error fullPageParam is available in full page mode + const configuration = this.context.mode.fullPageParam as Record< + string, + string + >; + if (configuration) { + const pageParam = configuration[key]; + if (pageParam && typeof pageParam === "string") { + return pageParam; // Return the raw value if it exists and is a string + } + } + // Return an empty string if the key doesn't exist or the value is invalid + return ""; + } +} diff --git a/packages/pcf-context-service/tsconfig.json b/packages/pcf-context-service/tsconfig.json new file mode 100644 index 0000000..a8a5881 --- /dev/null +++ b/packages/pcf-context-service/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "declaration": true, + "outDir": "dist", + "strict": true + }, + "include": ["src/**/*"] +} diff --git a/packages/pcf-mocks/package.json b/packages/pcf-mocks/package.json new file mode 100644 index 0000000..8662715 --- /dev/null +++ b/packages/pcf-mocks/package.json @@ -0,0 +1,6 @@ +{ + "name": "pcf-mocks", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts" +} diff --git a/packages/pcf-mocks/src/MockPCFClient.ts b/packages/pcf-mocks/src/MockPCFClient.ts new file mode 100644 index 0000000..fc41ada --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFClient.ts @@ -0,0 +1,39 @@ +export interface MockPCFClientOptions { + disableScroll?: boolean; + formFactor?: number; + client?: string; + offline?: boolean; + networkAvailable?: boolean; +} + +export class MockPCFClient implements ComponentFramework.Client { + disableScroll: boolean; + private formFactor: number; + private client: string; + private offline: boolean; + private networkAvailable: boolean; + + constructor(options: MockPCFClientOptions = {}) { + this.disableScroll = options.disableScroll ?? false; + this.formFactor = options.formFactor ?? 1; + this.client = options.client ?? "Web"; + this.offline = options.offline ?? false; + this.networkAvailable = options.networkAvailable ?? true; + } + + getFormFactor(): number { + return this.formFactor; + } + + getClient(): string { + return this.client; + } + + isOffline(): boolean { + return this.offline; + } + + isNetworkAvailable(): boolean { + return this.networkAvailable; + } +} diff --git a/packages/pcf-mocks/src/MockPCFContext.ts b/packages/pcf-mocks/src/MockPCFContext.ts new file mode 100644 index 0000000..e10a376 --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFContext.ts @@ -0,0 +1,43 @@ +import { MockPCFClient } from "./MockPCFClient"; +import { MockPCFDevice } from "./MockPCFDevice"; +import { MockPCFFactory } from "./MockPCFFactory"; +import { MockPCFFormatting } from "./MockPCFFormatting"; +import { MockPCFMode } from "./MockPCFMode"; +import { MockPCFNavigation } from "./MockPCFNavigation"; +import { MockPCFResources } from "./MockPCFResources"; +import { MockPCFUserSettings } from "./MockPCFUserSettings"; +import { MockPCFUtility } from "./MockPCFUtility"; +import { MockPCFWebApi } from "./MockPCFWebApi"; +import { MockPCFParameters, MockPCFParameterValue } from "./MockPCFParameters"; + +type IEvents = ComponentFramework.IEventBag; + +export class MockPCFContext implements ComponentFramework.Context { + constructor(initialParams?: Record) { + this.client = new MockPCFClient(); + this.device = new MockPCFDevice(); + this.factory = new MockPCFFactory(); + this.formatting = new MockPCFFormatting(); + this.mode = new MockPCFMode(); + this.navigation = new MockPCFNavigation(); + this.resources = new MockPCFResources(); + this.userSettings = new MockPCFUserSettings(); + this.utils = new MockPCFUtility(); + this.webAPI = new MockPCFWebApi(); + this.events = {} as IEvents; + this.parameters = new MockPCFParameters(initialParams) as unknown as TInputs; + } + client: ComponentFramework.Client; + device: ComponentFramework.Device; + factory: ComponentFramework.Factory; + formatting: ComponentFramework.Formatting; + mode: ComponentFramework.Mode; + navigation: ComponentFramework.Navigation; + resources: ComponentFramework.Resources; + userSettings: ComponentFramework.UserSettings; + utils: ComponentFramework.Utility; + webAPI: ComponentFramework.WebApi; + parameters: TInputs; + updatedProperties: string[] = []; + events: IEvents; +} diff --git a/packages/pcf-mocks/src/MockPCFDataSet.ts b/packages/pcf-mocks/src/MockPCFDataSet.ts new file mode 100644 index 0000000..78f0aad --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFDataSet.ts @@ -0,0 +1,54 @@ +/** + * Minimal mock implementation of a PCF DataSet property for testing. + * Allows you to set columns, records, and basic paging/filtering logic. + */ +export class MockPCFDataSet implements ComponentFramework.PropertyTypes.DataSet { + columns: ComponentFramework.PropertyHelper.DataSetApi.Column[] = []; + error = false; + errorMessage = ""; + filtering: ComponentFramework.PropertyHelper.DataSetApi.Filtering; + linking: ComponentFramework.PropertyHelper.DataSetApi.Linking; + loading = false; + paging: ComponentFramework.PropertyHelper.DataSetApi.Paging; + records: { [id: string]: ComponentFramework.PropertyHelper.DataSetApi.EntityRecord } = {}; + sortedRecordIds: string[] = []; + sorting: ComponentFramework.PropertyHelper.DataSetApi.SortStatus[] = []; + + constructor(options?: Partial) { + Object.assign(this, options); + + // Provide minimal no-op implementations for required methods if not supplied + this.filtering = this.filtering ?? { + getFilter: () => ({ conditions: [], filterOperator: 0 }), + setFilter: () => {}, + clearFilter: () => {}, + }; + this.linking = this.linking ?? { + getLinkedEntities: () => [], + addLinkedEntity: () => {}, + }; + this.paging = this.paging ?? { + totalResultCount: 0, + firstPageNumber: 1, + lastPageNumber: 1, + pageSize: 50, + hasNextPage: false, + hasPreviousPage: false, + loadNextPage: () => {}, + loadPreviousPage: () => {}, + reset: () => {}, + setPageSize: () => {}, + loadExactPage: () => {}, + }; + this.sortedRecordIds = this.sortedRecordIds ?? Object.keys(this.records); + } + + clearSelectedRecordIds(): void {} + getSelectedRecordIds(): string[] { return []; } + getTargetEntityType(): string { return "mockentity"; } + getTitle(): string { return "Mock DataSet"; } + getViewId(): string { return "mock-view-id"; } + openDatasetItem(entityReference: ComponentFramework.EntityReference): void {} + refresh(): void {} + setSelectedRecordIds(ids: string[]): void {} +} diff --git a/packages/pcf-mocks/src/MockPCFDevice.ts b/packages/pcf-mocks/src/MockPCFDevice.ts new file mode 100644 index 0000000..ee589da --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFDevice.ts @@ -0,0 +1,48 @@ +export interface MockPCFDeviceOptions { + barcodeValue?: string; + position?: ComponentFramework.DeviceApi.Position; + fileObject?: ComponentFramework.FileObject; + fileObjects?: ComponentFramework.FileObject[]; +} + +export class MockPCFDevice implements ComponentFramework.Device { + private barcodeValue: string; + private position: ComponentFramework.DeviceApi.Position; + private fileObject: ComponentFramework.FileObject; + private fileObjects: ComponentFramework.FileObject[]; + + constructor(options: MockPCFDeviceOptions = {}) { + this.barcodeValue = options.barcodeValue ?? "mock-barcode"; + this.position = options.position ?? ({} as ComponentFramework.DeviceApi.Position); + this.fileObject = options.fileObject ?? ({} as ComponentFramework.FileObject); + this.fileObjects = options.fileObjects ?? []; + } + + captureAudio(): Promise { + return Promise.resolve(this.fileObject); + } + + captureImage( + _options?: ComponentFramework.DeviceApi.CaptureImageOptions | undefined, + ): Promise { + return Promise.resolve(this.fileObject); + } + + captureVideo(): Promise { + return Promise.resolve(this.fileObject); + } + + getBarcodeValue(): Promise { + return Promise.resolve(this.barcodeValue); + } + + getCurrentPosition(): Promise { + return Promise.resolve(this.position); + } + + pickFile( + _options?: ComponentFramework.DeviceApi.PickFileOptions | undefined, + ): Promise { + return Promise.resolve(this.fileObjects); + } +} diff --git a/packages/pcf-mocks/src/MockPCFFactory.ts b/packages/pcf-mocks/src/MockPCFFactory.ts new file mode 100644 index 0000000..353be89 --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFFactory.ts @@ -0,0 +1,29 @@ +export interface MockPCFFactoryOptions { + getPopupService?: () => ComponentFramework.FactoryApi.Popup.PopupService; + requestRender?: () => void; +} + +export class MockPCFFactory implements ComponentFramework.Factory { + private _getPopupService?: () => ComponentFramework.FactoryApi.Popup.PopupService; + private _requestRender?: () => void; + + constructor(options: MockPCFFactoryOptions = {}) { + this._getPopupService = options.getPopupService; + this._requestRender = options.requestRender; + } + + getPopupService(): ComponentFramework.FactoryApi.Popup.PopupService { + if (this._getPopupService) { + return this._getPopupService(); + } + throw new Error("Method not implemented."); + } + + requestRender(): void { + if (this._requestRender) { + this._requestRender(); + return; + } + throw new Error("Method not implemented."); + } +} diff --git a/packages/pcf-mocks/src/MockPCFFormatting.ts b/packages/pcf-mocks/src/MockPCFFormatting.ts new file mode 100644 index 0000000..7af70bc --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFFormatting.ts @@ -0,0 +1,109 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +export interface MockPCFFormattingOptions { + formatCurrency?: (value: number, precision?: number, symbol?: string) => string; + formatDecimal?: (value: number, precision?: number) => string; + formatDateAsFilterStringInUTC?: (value: Date, includeTime?: boolean) => string; + formatDateLong?: (value: Date) => string; + formatDateLongAbbreviated?: (value: Date) => string; + formatDateShort?: (value: Date, includeTime?: boolean) => string; + formatDateYearMonth?: (value: Date) => string; + formatInteger?: (value: number) => string; + formatLanguage?: (value: number) => string; + formatTime?: (value: Date, behavior: ComponentFramework.FormattingApi.Types.DateTimeFieldBehavior) => string; + getWeekOfYear?: (value: Date) => number; +} + +export class MockPCFFormatting implements ComponentFramework.Formatting { + private _formatCurrency?: (value: number, precision?: number, symbol?: string) => string; + private _formatDecimal?: (value: number, precision?: number) => string; + private _formatDateAsFilterStringInUTC?: (value: Date, includeTime?: boolean) => string; + private _formatDateLong?: (value: Date) => string; + private _formatDateLongAbbreviated?: (value: Date) => string; + private _formatDateShort?: (value: Date, includeTime?: boolean) => string; + private _formatDateYearMonth?: (value: Date) => string; + private _formatInteger?: (value: number) => string; + private _formatLanguage?: (value: number) => string; + private _formatTime?: (value: Date, behavior: ComponentFramework.FormattingApi.Types.DateTimeFieldBehavior) => string; + private _getWeekOfYear?: (value: Date) => number; + + constructor(options: MockPCFFormattingOptions = {}) { + this._formatCurrency = options.formatCurrency; + this._formatDecimal = options.formatDecimal; + this._formatDateAsFilterStringInUTC = options.formatDateAsFilterStringInUTC; + this._formatDateLong = options.formatDateLong; + this._formatDateLongAbbreviated = options.formatDateLongAbbreviated; + this._formatDateShort = options.formatDateShort; + this._formatDateYearMonth = options.formatDateYearMonth; + this._formatInteger = options.formatInteger; + this._formatLanguage = options.formatLanguage; + this._formatTime = options.formatTime; + this._getWeekOfYear = options.getWeekOfYear; + } + + formatCurrency(value: number, precision?: number, symbol?: string): string { + if (this._formatCurrency) { + return this._formatCurrency(value, precision, symbol); + } + throw new Error("Method not implemented."); + } + formatDecimal(value: number, precision?: number): string { + if (this._formatDecimal) { + return this._formatDecimal(value, precision); + } + throw new Error("Method not implemented."); + } + formatDateAsFilterStringInUTC(value: Date, includeTime?: boolean): string { + if (this._formatDateAsFilterStringInUTC) { + return this._formatDateAsFilterStringInUTC(value, includeTime); + } + throw new Error("Method not implemented."); + } + formatDateLong(value: Date): string { + if (this._formatDateLong) { + return this._formatDateLong(value); + } + throw new Error("Method not implemented."); + } + formatDateLongAbbreviated(value: Date): string { + if (this._formatDateLongAbbreviated) { + return this._formatDateLongAbbreviated(value); + } + throw new Error("Method not implemented."); + } + formatDateShort(value: Date, includeTime?: boolean): string { + if (this._formatDateShort) { + return this._formatDateShort(value, includeTime); + } + throw new Error("Method not implemented."); + } + formatDateYearMonth(value: Date): string { + if (this._formatDateYearMonth) { + return this._formatDateYearMonth(value); + } + throw new Error("Method not implemented."); + } + formatInteger(value: number): string { + if (this._formatInteger) { + return this._formatInteger(value); + } + throw new Error("Method not implemented."); + } + formatLanguage(value: number): string { + if (this._formatLanguage) { + return this._formatLanguage(value); + } + throw new Error("Method not implemented."); + } + formatTime(value: Date, behavior: ComponentFramework.FormattingApi.Types.DateTimeFieldBehavior): string { + if (this._formatTime) { + return this._formatTime(value, behavior); + } + throw new Error("Method not implemented."); + } + getWeekOfYear(value: Date): number { + if (this._getWeekOfYear) { + return this._getWeekOfYear(value); + } + throw new Error("Method not implemented."); + } +} diff --git a/packages/pcf-mocks/src/MockPCFMode.ts b/packages/pcf-mocks/src/MockPCFMode.ts new file mode 100644 index 0000000..883ec7c --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFMode.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +export interface MockPCFModeOptions { + allocatedHeight?: number; + allocatedWidth?: number; + isControlDisabled?: boolean; + isVisible?: boolean; + label?: string; + contextInfo?: { + entityId?: string; + entityTypeName?: string; + entityRecordName?: string; + [key: string]: any; + }; +} + +export class MockPCFMode implements ComponentFramework.Mode { + allocatedHeight: number; + allocatedWidth: number; + isControlDisabled: boolean; + isVisible: boolean; + label: string; + // Add contextInfo property for custom context usage + contextInfo?: { + entityId?: string; + entityTypeName?: string; + entityRecordName?: string; + [key: string]: any; + }; + + constructor(options: MockPCFModeOptions = {}) { + this.allocatedHeight = options.allocatedHeight ?? 600; + this.allocatedWidth = options.allocatedWidth ?? 800; + this.isControlDisabled = options.isControlDisabled ?? false; + this.isVisible = options.isVisible ?? true; + this.label = options.label ?? "Mock Mode"; + this.contextInfo = options.contextInfo; + } + + setControlState(state: ComponentFramework.Dictionary): boolean { + throw new Error("Method not implemented."); + } + setFullScreen(value: boolean): void { + throw new Error("Method not implemented."); + } + trackContainerResize(value: boolean): void { + throw new Error("Method not implemented."); + } +} diff --git a/packages/pcf-mocks/src/MockPCFNavigation.ts b/packages/pcf-mocks/src/MockPCFNavigation.ts new file mode 100644 index 0000000..a529601 --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFNavigation.ts @@ -0,0 +1,144 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +export interface MockPCFNavigationOptions { + openAlertDialog?: ( + alertStrings: ComponentFramework.NavigationApi.AlertDialogStrings, + options?: ComponentFramework.NavigationApi.AlertDialogOptions + ) => Promise; + openConfirmDialog?: ( + confirmStrings: ComponentFramework.NavigationApi.ConfirmDialogStrings, + options?: ComponentFramework.NavigationApi.ConfirmDialogOptions + ) => Promise; + openErrorDialog?: ( + options: ComponentFramework.NavigationApi.ErrorDialogOptions + ) => Promise; + openFile?: ( + file: ComponentFramework.FileObject, + options?: ComponentFramework.NavigationApi.OpenFileOptions + ) => Promise; + openForm?: ( + options: ComponentFramework.NavigationApi.EntityFormOptions, + parameters?: { [key: string]: string } + ) => Promise; + openUrl?: ( + url: string, + options?: ComponentFramework.NavigationApi.OpenUrlOptions + ) => void; + openWebResource?: ( + name: string, + options?: ComponentFramework.NavigationApi.OpenWebResourceOptions, + data?: string + ) => void; +} + +export class MockPCFNavigation implements ComponentFramework.Navigation { + private _openAlertDialog?: ( + alertStrings: ComponentFramework.NavigationApi.AlertDialogStrings, + options?: ComponentFramework.NavigationApi.AlertDialogOptions + ) => Promise; + private _openConfirmDialog?: ( + confirmStrings: ComponentFramework.NavigationApi.ConfirmDialogStrings, + options?: ComponentFramework.NavigationApi.ConfirmDialogOptions + ) => Promise; + private _openErrorDialog?: ( + options: ComponentFramework.NavigationApi.ErrorDialogOptions + ) => Promise; + private _openFile?: ( + file: ComponentFramework.FileObject, + options?: ComponentFramework.NavigationApi.OpenFileOptions + ) => Promise; + private _openForm?: ( + options: ComponentFramework.NavigationApi.EntityFormOptions, + parameters?: { [key: string]: string } + ) => Promise; + private _openUrl?: ( + url: string, + options?: ComponentFramework.NavigationApi.OpenUrlOptions + ) => void; + private _openWebResource?: ( + name: string, + options?: ComponentFramework.NavigationApi.OpenWebResourceOptions, + data?: string + ) => void; + + constructor(options: MockPCFNavigationOptions = {}) { + this._openAlertDialog = options.openAlertDialog; + this._openConfirmDialog = options.openConfirmDialog; + this._openErrorDialog = options.openErrorDialog; + this._openFile = options.openFile; + this._openForm = options.openForm; + this._openUrl = options.openUrl; + this._openWebResource = options.openWebResource; + } + + openAlertDialog( + alertStrings: ComponentFramework.NavigationApi.AlertDialogStrings, + options?: ComponentFramework.NavigationApi.AlertDialogOptions + ): Promise { + if (this._openAlertDialog) { + return this._openAlertDialog(alertStrings, options); + } + throw new Error("Method not implemented."); + } + + openConfirmDialog( + confirmStrings: ComponentFramework.NavigationApi.ConfirmDialogStrings, + options?: ComponentFramework.NavigationApi.ConfirmDialogOptions + ): Promise { + if (this._openConfirmDialog) { + return this._openConfirmDialog(confirmStrings, options); + } + throw new Error("Method not implemented."); + } + + openErrorDialog( + options: ComponentFramework.NavigationApi.ErrorDialogOptions + ): Promise { + if (this._openErrorDialog) { + return this._openErrorDialog(options); + } + throw new Error("Method not implemented."); + } + + openFile( + file: ComponentFramework.FileObject, + options?: ComponentFramework.NavigationApi.OpenFileOptions + ): Promise { + if (this._openFile) { + return this._openFile(file, options); + } + throw new Error("Method not implemented."); + } + + openForm( + options: ComponentFramework.NavigationApi.EntityFormOptions, + parameters?: { [key: string]: string } + ): Promise { + if (this._openForm) { + return this._openForm(options, parameters); + } + throw new Error("Method not implemented."); + } + + openUrl( + url: string, + options?: ComponentFramework.NavigationApi.OpenUrlOptions + ): void { + if (this._openUrl) { + this._openUrl(url, options); + return; + } + throw new Error("Method not implemented."); + } + + openWebResource( + name: string, + options?: ComponentFramework.NavigationApi.OpenWebResourceOptions, + data?: string + ): void { + if (this._openWebResource) { + this._openWebResource(name, options, data); + return; + } + throw new Error("Method not implemented."); + } +} diff --git a/packages/pcf-mocks/src/MockPCFParameters.ts b/packages/pcf-mocks/src/MockPCFParameters.ts new file mode 100644 index 0000000..a50d126 --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFParameters.ts @@ -0,0 +1,40 @@ +import { MockPCFDataSet } from "./MockPCFDataSet"; + +/** + * MockPCFParameterValue represents the structure of a PCF parameter value. + */ +export type MockPCFParameterValue = { + raw: T; + formatted?: string; + [key: string]: any; +}; + +/** + * MockPCFParameters provides a generic, flexible way to mock PCF context parameters. + * Supports both params.getParameter("key") and params.key access. + */ +export class MockPCFParameters { + [key: string]: MockPCFParameterValue | any; + + constructor(initialParams?: Record) { + if (initialParams) { + for (const key of Object.keys(initialParams)) { + this[key] = initialParams[key]; + } + } + } + + /** + * Set a parameter value. + */ + setParameter(key: string, value: MockPCFParameterValue) { + this[key] = value; + } + + /** + * Get a parameter value. + */ + getParameter(key: string): MockPCFParameterValue { + return this[key]; + } +} diff --git a/packages/pcf-mocks/src/MockPCFResources.ts b/packages/pcf-mocks/src/MockPCFResources.ts new file mode 100644 index 0000000..16d3f5e --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFResources.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +export interface MockPCFResourcesOptions { + getResource?: (id: string, success: (data: string) => void, failure: () => void) => void; + getString?: (id: string) => string; +} + +export class MockPCFResources implements ComponentFramework.Resources { + private _getResource?: (id: string, success: (data: string) => void, failure: () => void) => void; + private _getString?: (id: string) => string; + + constructor(options: MockPCFResourcesOptions = {}) { + this._getResource = options.getResource; + this._getString = options.getString; + } + + getResource(id: string, success: (data: string) => void, failure: () => void): void { + if (this._getResource) { + this._getResource(id, success, failure); + } else { + throw new Error("Method not implemented."); + } + } + + getString(id: string): string { + if (this._getString) { + return this._getString(id); + } + throw new Error("Method not implemented."); + } +} diff --git a/packages/pcf-mocks/src/MockPCFUserSettings.ts b/packages/pcf-mocks/src/MockPCFUserSettings.ts new file mode 100644 index 0000000..b0d201f --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFUserSettings.ts @@ -0,0 +1,40 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +export interface MockPCFUserSettingsOptions { + dateFormattingInfo?: ComponentFramework.UserSettingApi.DateFormattingInfo; + isRTL?: boolean; + languageId?: number; + numberFormattingInfo?: ComponentFramework.UserSettingApi.NumberFormattingInfo; + securityRoles?: string[]; + userId?: string; + userName?: string; + getTimeZoneOffsetMinutes?: (date?: Date) => number; +} + +export class MockPCFUserSettings implements ComponentFramework.UserSettings { + dateFormattingInfo: ComponentFramework.UserSettingApi.DateFormattingInfo; + isRTL: boolean; + languageId: number; + numberFormattingInfo: ComponentFramework.UserSettingApi.NumberFormattingInfo; + securityRoles: string[]; + userId: string; + userName: string; + private _getTimeZoneOffsetMinutes?: (date?: Date) => number; + + constructor(options: MockPCFUserSettingsOptions = {}) { + this.dateFormattingInfo = options.dateFormattingInfo ?? {} as ComponentFramework.UserSettingApi.DateFormattingInfo; + this.isRTL = options.isRTL ?? false; + this.languageId = options.languageId ?? 1033; + this.numberFormattingInfo = options.numberFormattingInfo ?? {} as ComponentFramework.UserSettingApi.NumberFormattingInfo; + this.securityRoles = options.securityRoles ?? []; + this.userId = options.userId ?? "mock-user-id"; + this.userName = options.userName ?? "Mock User"; + this._getTimeZoneOffsetMinutes = options.getTimeZoneOffsetMinutes; + } + + getTimeZoneOffsetMinutes(date?: Date | undefined): number { + if (this._getTimeZoneOffsetMinutes) { + return this._getTimeZoneOffsetMinutes(date); + } + throw new Error("Method not implemented."); + } +} diff --git a/packages/pcf-mocks/src/MockPCFUtility.ts b/packages/pcf-mocks/src/MockPCFUtility.ts new file mode 100644 index 0000000..f19c985 --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFUtility.ts @@ -0,0 +1,66 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +export interface MockPCFUtilityOptions { + getEntityMetadata?: ( + entityName: string, + attributes?: string[] + ) => Promise; + hasEntityPrivilege?: ( + entityTypeName: string, + privilegeType: ComponentFramework.PropertyHelper.Types.PrivilegeType, + privilegeDepth: ComponentFramework.PropertyHelper.Types.PrivilegeDepth + ) => boolean; + lookupObjects?: ( + lookupOptions: ComponentFramework.UtilityApi.LookupOptions + ) => Promise; +} + +export class MockPCFUtility implements ComponentFramework.Utility { + private _getEntityMetadata?: ( + entityName: string, + attributes?: string[] + ) => Promise; + private _hasEntityPrivilege?: ( + entityTypeName: string, + privilegeType: ComponentFramework.PropertyHelper.Types.PrivilegeType, + privilegeDepth: ComponentFramework.PropertyHelper.Types.PrivilegeDepth + ) => boolean; + private _lookupObjects?: ( + lookupOptions: ComponentFramework.UtilityApi.LookupOptions + ) => Promise; + + constructor(options: MockPCFUtilityOptions = {}) { + this._getEntityMetadata = options.getEntityMetadata; + this._hasEntityPrivilege = options.hasEntityPrivilege; + this._lookupObjects = options.lookupObjects; + } + + getEntityMetadata( + entityName: string, + attributes?: string[] | undefined, + ): Promise { + if (this._getEntityMetadata) { + return this._getEntityMetadata(entityName, attributes); + } + throw new Error("Method not implemented."); + } + + hasEntityPrivilege( + entityTypeName: string, + privilegeType: ComponentFramework.PropertyHelper.Types.PrivilegeType, + privilegeDepth: ComponentFramework.PropertyHelper.Types.PrivilegeDepth, + ): boolean { + if (this._hasEntityPrivilege) { + return this._hasEntityPrivilege(entityTypeName, privilegeType, privilegeDepth); + } + throw new Error("Method not implemented."); + } + + lookupObjects( + lookupOptions: ComponentFramework.UtilityApi.LookupOptions, + ): Promise { + if (this._lookupObjects) { + return this._lookupObjects(lookupOptions); + } + throw new Error("Method not implemented."); + } +} diff --git a/packages/pcf-mocks/src/MockPCFWebApi.ts b/packages/pcf-mocks/src/MockPCFWebApi.ts new file mode 100644 index 0000000..a15f43d --- /dev/null +++ b/packages/pcf-mocks/src/MockPCFWebApi.ts @@ -0,0 +1,112 @@ +export interface MockPCFWebApiOptions { + createRecord?: ( + entityType: string, + data: ComponentFramework.WebApi.Entity + ) => Promise; + deleteRecord?: ( + entityType: string, + id: string + ) => Promise; + updateRecord?: ( + entityType: string, + id: string, + data: ComponentFramework.WebApi.Entity + ) => Promise; + retrieveMultipleRecords?: ( + entityType: string, + options?: string, + maxPageSize?: number + ) => Promise; + retrieveRecord?: ( + entityType: string, + id: string, + options?: string + ) => Promise; +} + +export class MockPCFWebApi implements ComponentFramework.WebApi { + private _createRecord?: ( + entityType: string, + data: ComponentFramework.WebApi.Entity + ) => Promise; + private _deleteRecord?: ( + entityType: string, + id: string + ) => Promise; + private _updateRecord?: ( + entityType: string, + id: string, + data: ComponentFramework.WebApi.Entity + ) => Promise; + private _retrieveMultipleRecords?: ( + entityType: string, + options?: string, + maxPageSize?: number + ) => Promise; + private _retrieveRecord?: ( + entityType: string, + id: string, + options?: string + ) => Promise; + + constructor(options: MockPCFWebApiOptions = {}) { + this._createRecord = options.createRecord; + this._deleteRecord = options.deleteRecord; + this._updateRecord = options.updateRecord; + this._retrieveMultipleRecords = options.retrieveMultipleRecords; + this._retrieveRecord = options.retrieveRecord; + } + + createRecord( + entityType: string, + data: ComponentFramework.WebApi.Entity + ): Promise { + if (this._createRecord) { + return this._createRecord(entityType, data); + } + throw new Error("Method not implemented."); + } + + deleteRecord( + entityType: string, + id: string + ): Promise { + if (this._deleteRecord) { + return this._deleteRecord(entityType, id); + } + throw new Error("Method not implemented."); + } + + updateRecord( + entityType: string, + id: string, + data: ComponentFramework.WebApi.Entity + ): Promise { + if (this._updateRecord) { + return this._updateRecord(entityType, id, data); + } + throw new Error("Method not implemented."); + } + + retrieveMultipleRecords( + entityType: string, + options?: string, + maxPageSize?: number + ): Promise { + if (this._retrieveMultipleRecords) { + return this._retrieveMultipleRecords(entityType, options, maxPageSize); + } + throw new Error("Method not implemented."); + } + + retrieveRecord( + entityType: string, + id: string, + options?: string + ): Promise { + if (this._retrieveRecord) { + return this._retrieveRecord(entityType, id, options); + } + throw new Error("Method not implemented."); + } +} diff --git a/packages/pcf-mocks/src/index.ts b/packages/pcf-mocks/src/index.ts new file mode 100644 index 0000000..fe3c075 --- /dev/null +++ b/packages/pcf-mocks/src/index.ts @@ -0,0 +1,13 @@ +export * from './MockPCFClient'; +export * from './MockPCFContext'; +export * from './MockPCFDataSet'; +export * from './MockPCFDevice'; +export * from './MockPCFFactory'; +export * from './MockPCFFormatting'; +export * from './MockPCFMode'; +export * from './MockPCFNavigation'; +export * from './MockPCFParameters'; +export * from './MockPCFResources'; +export * from './MockPCFUserSettings'; +export * from './MockPCFUtility'; +export * from './MockPCFWebApi'; diff --git a/packages/pcf-mocks/tsconfig.json b/packages/pcf-mocks/tsconfig.json new file mode 100644 index 0000000..a8a5881 --- /dev/null +++ b/packages/pcf-mocks/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "declaration": true, + "outDir": "dist", + "strict": true + }, + "include": ["src/**/*"] +}