├── .eslintignore ├── generators └── microsoft-employee-experience-generator │ ├── examples │ ├── .nvmrc │ ├── src │ │ ├── Components │ │ │ ├── Main │ │ │ │ ├── index.ts │ │ │ │ └── Main.ts │ │ │ ├── Nav │ │ │ │ ├── index.ts │ │ │ │ ├── Nav.types.ts │ │ │ │ ├── Nav.tsx │ │ │ │ └── useCoherenceNavGroups.ts │ │ │ └── Header │ │ │ │ ├── index.ts │ │ │ │ ├── Header.styled.ts │ │ │ │ ├── Header.types.ts │ │ │ │ └── Header.tsx │ │ ├── Samples │ │ │ ├── DynamicSubRoutes │ │ │ │ ├── config.js │ │ │ │ ├── DynamicRouteParamConsumer.tsx │ │ │ │ └── DynamicSubRoutes.tsx │ │ │ ├── CodeSplitting │ │ │ │ ├── MyProfile.tsx │ │ │ │ └── CodeSplitting.tsx │ │ │ ├── Shared │ │ │ │ ├── SharedExample.actions.ts │ │ │ │ ├── SharedExample.action-types.ts │ │ │ │ ├── Layout.ts │ │ │ │ ├── SharedExample.types.ts │ │ │ │ ├── SharedExample.sagas.ts │ │ │ │ └── SharedExample.reducer.ts │ │ │ ├── BuildOnceAuthClient.ts │ │ │ ├── DynamicReduxHooks │ │ │ │ └── DynamicReduxHooks.tsx │ │ │ ├── AdalClient.ts │ │ │ └── MSALClient.ts │ │ ├── navConfig.ts │ │ ├── Routes.tsx │ │ ├── useHeaderConfig.tsx │ │ ├── App.tsx │ │ └── ShellWithStore.tsx │ ├── prettier.config.js │ ├── babel.config.js │ ├── tsconfig.json │ ├── global.d.ts │ ├── .npmrc │ ├── jest.config.js │ ├── .eslintrc.js │ ├── README.md │ ├── .npmignore │ ├── .gitignore │ ├── webpack.config.js │ └── package.json │ ├── yo │ ├── .yo-rc.json │ ├── package.json │ └── generators │ │ └── app │ │ └── index.js │ ├── package.json │ ├── README.md │ ├── .gitattributes │ └── .gitignore ├── extensions └── microsoft-employee-experience │ ├── src │ ├── Shell │ │ ├── index.ts │ │ ├── Shell.styled.ts │ │ └── Shell.tsx │ ├── AuthClient │ │ ├── index.ts │ │ └── AuthClient.types.ts │ ├── Checkbox │ │ ├── index.ts │ │ ├── Checkbox.types.ts │ │ └── Checkbox.tsx │ ├── DatePicker │ │ ├── index.ts │ │ ├── DatePicker.types.ts │ │ └── DatePicker.tsx │ ├── Dropdown │ │ ├── index.ts │ │ ├── Dropdown.types.ts │ │ └── Dropdown.tsx │ ├── TextField │ │ ├── index.ts │ │ ├── TextField.types.ts │ │ └── TextField.tsx │ ├── OnBuildOnce │ │ ├── index.ts │ │ └── OnBuildOnce.tsx │ ├── Context.tsx │ ├── WebpackConfigs │ │ ├── index.ts │ │ └── WebpackConfigs.utils.ts │ ├── Link │ │ ├── index.ts │ │ ├── Link.utils.ts │ │ ├── Link.types.ts │ │ └── Link.tsx │ ├── IDefaultState.ts │ ├── StoreBuilder.ts │ ├── IComponentConfig.ts │ ├── IComponentProps.ts │ ├── Persona │ │ ├── index.ts │ │ ├── Persona.tsx │ │ └── Persona.types.ts │ ├── ReducerRegistry.ts │ ├── ComponentProvider.ts │ ├── IReducerRegistry.ts │ ├── useDynamicReducer.ts │ ├── CodeSplitter │ │ ├── index.ts │ │ ├── CodeSplitter.types.ts │ │ └── CodeSplitter.tsx │ ├── IUser.ts │ ├── InputGroup.ts │ ├── UsageTelemetry │ │ ├── UsageFeatureProps.ts │ │ ├── UsageLog.ts │ │ ├── Config │ │ │ └── UsageTelemetryConfig.ts │ │ ├── UsageEvent.ts │ │ ├── UsageEvent │ │ │ ├── AwareEvent.ts │ │ │ ├── UserEvent.ts │ │ │ └── SytemEvent.ts │ │ ├── index.ts │ │ ├── UserAttribute.ts │ │ ├── Helpers │ │ │ ├── CuppSchemaMapper.ts │ │ │ └── UsageEventName.ts │ │ └── UsageTracker.ts │ ├── usePageTitle.ts │ ├── IUsageClient.ts │ ├── Buttons │ │ ├── IButtonProps.ts │ │ ├── IconButton.ts │ │ ├── ActionButton.ts │ │ ├── PrimaryButton.ts │ │ ├── DefaultButton.ts │ │ ├── withButtonClickLogging.types.ts │ │ └── withButtonClickLogging.tsx │ ├── IUTPConfig.ts │ ├── IGraphClient.ts │ ├── IAuthClient.ts │ ├── ITelemetryContext.ts │ ├── useUser.ts │ ├── RouteComponentProvider.tsx │ ├── IEmployeeExperienceContext.ts │ ├── useGraphPhoto.ts │ ├── withStore.tsx │ ├── useUsageTelemetry.ts │ ├── TelemetryEvents.ts │ ├── useLoginOnStartup.ts │ ├── GraphClient.ts │ ├── usePageTracking.ts │ ├── IHttpClient.ts │ ├── UsageClient.ts │ ├── UsageTelemetryHelper.ts │ └── ITelemetryClient.ts │ ├── tsconfig.json │ └── package.json ├── packages ├── micro-frontend-react │ ├── src │ │ ├── ComponentProvider │ │ │ ├── index.ts │ │ │ └── ComponentProvider.types.ts │ │ ├── WebpackConfigs │ │ │ ├── index.ts │ │ │ ├── WebpackConfigs.utils.ts │ │ │ └── WebpackConfigs.ts │ │ ├── IComponentConfig.ts │ │ ├── IComponentProps.ts │ │ └── Context.tsx │ ├── tsconfig.json │ └── package.json └── micro-frontend-react-redux │ ├── src │ ├── Context.tsx │ ├── IComponentConfig.ts │ ├── IComponentProps.ts │ ├── ComponentProvider.ts │ ├── WebpackConfigs.ts │ ├── IDefaultState.ts │ ├── IStoreBuilderResult.ts │ ├── InjectReduxContext.ts │ ├── IReduxContext.ts │ ├── useDynamicReducer.ts │ ├── IReducerRegistry.ts │ ├── IStoreBuilder.ts │ └── ReducerRegistry.ts │ ├── tsconfig.json │ └── package.json ├── samples ├── sample-react-redux-micro-frontend │ ├── global.d.ts │ ├── package.json │ ├── tsconfig.json │ ├── webpack.config.js │ └── src │ │ ├── SampleMicroFrontendReduxStore.ts │ │ └── MicroFrontendApp.tsx ├── sample-react-redux-host │ ├── public │ │ └── api │ │ │ └── user.json │ ├── global.d.ts │ ├── src │ │ ├── DummyHttpClient.ts │ │ ├── SampleHostReduxStore.ts │ │ ├── Home.tsx │ │ └── App.tsx │ ├── tsconfig.json │ ├── package.json │ └── webpack.config.js ├── sample-react-host │ ├── global.d.ts │ ├── tsconfig.json │ ├── package.json │ ├── src │ │ ├── Home.tsx │ │ └── App.tsx │ └── webpack.config.js └── sample-react-micro-frontend │ ├── global.d.ts │ ├── package.json │ ├── tsconfig.json │ ├── webpack.config.js │ └── src │ └── MicroFrontendApp.tsx ├── prettier.config.js ├── babel.config.js ├── lerna.json ├── test-result.config.js ├── CODE_OF_CONDUCT.md ├── lage.config.js ├── .eslintrc.js ├── jest.config.js ├── LICENSE ├── SUPPORT.md ├── .gitignore ├── .gitattributes ├── package.json ├── SECURITY.md └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.js 3 | samples/*.ts 4 | lib 5 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/.nvmrc: -------------------------------------------------------------------------------- 1 | v14.19.0 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Shell/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Shell'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/AuthClient/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AuthClient'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Checkbox/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Checkbox'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/DatePicker/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DatePicker'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Dropdown/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Dropdown'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/TextField/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TextField'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/OnBuildOnce/index.ts: -------------------------------------------------------------------------------- 1 | export * from './OnBuildOnce'; 2 | -------------------------------------------------------------------------------- /packages/micro-frontend-react/src/ComponentProvider/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ComponentProvider'; 2 | -------------------------------------------------------------------------------- /packages/micro-frontend-react/src/WebpackConfigs/index.ts: -------------------------------------------------------------------------------- 1 | module.exports = require('./WebpackConfigs'); 2 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/Context.tsx: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/core/lib/Context'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Context.tsx: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/core/lib/Context'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/WebpackConfigs/index.ts: -------------------------------------------------------------------------------- 1 | module.exports = require('./WebpackConfigs'); 2 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Components/Main/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Main'; 2 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Components/Nav/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Nav'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Link/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Link'; 2 | export * from './Link.utils'; 3 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Components/Header/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Header'; 2 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Samples/DynamicSubRoutes/config.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /samples/sample-react-redux-micro-frontend/global.d.ts: -------------------------------------------------------------------------------- 1 | export declare global { 2 | const __APP_NAME__: string; 3 | } 4 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/IDefaultState.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/redux/lib/IDefaultState'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/StoreBuilder.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/redux/lib/StoreBuilder'; 2 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/IComponentConfig.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/core/lib/IComponentConfig'; 2 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/IComponentProps.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/core/lib/IComponentProps'; 2 | -------------------------------------------------------------------------------- /samples/sample-react-redux-host/public/api/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sample User", 3 | "email": "sample@user.com" 4 | } 5 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/IComponentConfig.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/core/lib/IComponentConfig'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/IComponentProps.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/core/lib/IComponentProps'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Persona/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Persona'; 2 | export * from './Persona.types'; 3 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/ReducerRegistry.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/redux/lib/ReducerRegistry'; 2 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/ComponentProvider.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/core/lib/ComponentProvider'; 2 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | singleQuote: true, 4 | tabWidth: 2, 5 | printWidth: 120, 6 | }; 7 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/ComponentProvider.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/core/lib/ComponentProvider'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/IReducerRegistry.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/redux/lib/IReducerRegistry'; 2 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/useDynamicReducer.ts: -------------------------------------------------------------------------------- 1 | export * from '@micro-frontend-react/redux/lib/useDynamicReducer'; 2 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/WebpackConfigs.ts: -------------------------------------------------------------------------------- 1 | module.exports = require('@micro-frontend-react/core/lib/WebpackConfigs'); 2 | -------------------------------------------------------------------------------- /packages/micro-frontend-react/src/IComponentConfig.ts: -------------------------------------------------------------------------------- 1 | export interface IComponentConfig { 2 | name: string; 3 | script: string; 4 | } 5 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/CodeSplitter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CodeSplitter'; 2 | export * from './CodeSplitter.types'; 3 | -------------------------------------------------------------------------------- /samples/sample-react-redux-host/global.d.ts: -------------------------------------------------------------------------------- 1 | export declare global { 2 | const __IS_DEVELOPMENT__: boolean; 3 | const __APP_NAME__: string; 4 | } 5 | -------------------------------------------------------------------------------- /samples/sample-react-host/global.d.ts: -------------------------------------------------------------------------------- 1 | import * as styledUMD from 'styled-components'; 2 | 3 | declare global { 4 | const __APP_NAME__: string; 5 | } 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | test: { 4 | plugins: ['@babel/plugin-transform-modules-commonjs'], 5 | }, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/IUser.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | id: string; 3 | email: string; 4 | name: string; 5 | oid: string; 6 | } 7 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/*", "samples/*", "extensions/*"], 3 | "npmClient": "yarn", 4 | "useWorkspaces": true, 5 | "version": "1.0.15" 6 | } 7 | -------------------------------------------------------------------------------- /samples/sample-react-micro-frontend/global.d.ts: -------------------------------------------------------------------------------- 1 | import * as styledUMD from 'styled-components'; 2 | 3 | declare global { 4 | const __APP_NAME__: string; 5 | } 6 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/InputGroup.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const InputGroup = styled.div` 4 | margin-bottom: 24px; 5 | `; 6 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | singleQuote: true, 4 | tabWidth: 2, 5 | printWidth: 120, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/IDefaultState.ts: -------------------------------------------------------------------------------- 1 | export interface IDefaultState { 2 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 3 | dynamic?: { [key: string]: any }; 4 | } 5 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | test: { 4 | plugins: ['@babel/plugin-transform-modules-commonjs'], 5 | }, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /test-result.config.js: -------------------------------------------------------------------------------- 1 | var builder = require('jest-trx-results-processor/dist/testResultsProcessor'); 2 | 3 | var processor = builder({ 4 | outputFile: 'test-results.trx', 5 | }); 6 | 7 | module.exports = processor; 8 | -------------------------------------------------------------------------------- /packages/micro-frontend-react/src/IComponentProps.ts: -------------------------------------------------------------------------------- 1 | import { IComponentConfig } from './IComponentConfig'; 2 | 3 | export interface IComponentProps { 4 | config: IComponentConfig; 5 | context: T; 6 | } 7 | -------------------------------------------------------------------------------- /samples/sample-react-redux-host/src/DummyHttpClient.ts: -------------------------------------------------------------------------------- 1 | export class DummyHttpClient { 2 | async get(url: string): Promise { 3 | const response = await fetch(url); 4 | 5 | return response.json(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/UsageFeatureProps.ts: -------------------------------------------------------------------------------- 1 | export type UsageFeatureProps = { 2 | feature: string; 3 | subFeature: string; 4 | subFeatureLevel2?: string; 5 | featureLocation?: string; 6 | }; 7 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/usePageTitle.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | export function usePageTitle(title: string): void { 4 | useEffect(() => { 5 | window.document.title = title; 6 | }, [title]); 7 | } 8 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/yo/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-node": { 3 | "promptValues": { 4 | "authorName": "Won Song", 5 | "authorEmail": "wosong@microsoft.com", 6 | "authorUrl": "" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/IUsageClient.ts: -------------------------------------------------------------------------------- 1 | import { UsageTelemetryConfig } from './UsageTelemetry'; 2 | 3 | export interface IUsageClient { 4 | getUsageUserId(): Promise; 5 | 6 | getUsageConfig(): UsageTelemetryConfig; 7 | } 8 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Components/Header/Header.styled.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const HeaderRoot = styled.div` 4 | position: fixed; 5 | top: 0; 6 | width: 100%; 7 | z-index: 10000; 8 | `; 9 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Buttons/IButtonProps.ts: -------------------------------------------------------------------------------- 1 | import { IButtonProps as IFabricButtonProps } from '@fluentui/react/lib/Button'; 2 | 3 | export interface IButtonProps extends Pick> { 4 | title: string; 5 | } 6 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Components/Header/Header.types.ts: -------------------------------------------------------------------------------- 1 | export enum HeaderPanel { 2 | ProfilePanel = 'ProfilePanel', 3 | SettingsPanel = 'SettingsPanel', 4 | HelpPanel = 'HelpPanel', 5 | NotificationPanel = 'NotificationPanel', 6 | FeedbackPanel = 'FeedbackPanel', 7 | } 8 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/UsageLog.ts: -------------------------------------------------------------------------------- 1 | import { UsageEvent } from './UsageEvent'; 2 | import { UserAttribute } from './UserAttribute'; 3 | import { UsageTracker } from './UsageTracker'; 4 | 5 | export type UsageLog = UsageEvent & 6 | UserAttribute & 7 | UsageTracker & { feature: string }; 8 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Components/Main/Main.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Main = styled.main` 4 | width: 100%; 5 | padding-top: 48px; 6 | 7 | @media (min-width: 1025px) { 8 | width: calc(100% - 48px); 9 | margin-left: 48px; 10 | } 11 | `; 12 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/IUTPConfig.ts: -------------------------------------------------------------------------------- 1 | export interface IUTPConfig { 2 | UTPConfig?: { 3 | EnvironmentName: string; 4 | ServiceOffering: string; 5 | ServiceLine: string; 6 | Service: string; 7 | ComponentName: string; 8 | ComponentId: string; 9 | [key: string]: string; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/IGraphClient.ts: -------------------------------------------------------------------------------- 1 | export type GraphPhotoSize = 48 | 64 | 96 | 120 | 240 | 360 | 432 | 504 | 648 | undefined; 2 | 3 | export interface IGraphClient { 4 | getPhoto(upn: string, size?: GraphPhotoSize): Promise; 5 | } 6 | 7 | export interface IGraphClientOptions { 8 | baseUrl: string; 9 | resourceUri: string; 10 | } 11 | -------------------------------------------------------------------------------- /samples/sample-react-host/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "sourceMap": true, 8 | "strictNullChecks": false, 9 | "jsx": "react", 10 | "lib": ["dom", "es6"], 11 | "skipLibCheck": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample-react-redux-host/src/SampleHostReduxStore.ts: -------------------------------------------------------------------------------- 1 | export type HostState = { 2 | hostAppName: string; 3 | }; 4 | 5 | const initialState: HostState = { 6 | hostAppName: __APP_NAME__, 7 | }; 8 | 9 | export function hostReducer(prev: HostState = initialState, action: { type: string }) { 10 | switch (action.type) { 11 | default: 12 | return prev; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/CodeSplitter/CodeSplitter.types.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export interface ICodeSplitterProps { 4 | name: string; 5 | 6 | import(): Promise<{ [key: string]: React.ComponentType }>; 7 | 8 | props?: T; 9 | } 10 | 11 | export interface ICodeSplitterState { 12 | Component: React.ComponentType | null; 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample-react-redux-host/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "sourceMap": true, 8 | "strictNullChecks": false, 9 | "jsx": "react", 10 | "lib": ["dom", "es6"], 11 | "skipLibCheck": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample-react-micro-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@micro-frontend-react/sample-react-micro-frontend", 3 | "private": true, 4 | "version": "1.0.15", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "serve": "webpack-dev-server" 9 | }, 10 | "license": "MIT", 11 | "dependencies": { 12 | "@micro-frontend-react/core": "^1.0.15" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /samples/sample-react-micro-frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "sourceMap": true, 8 | "strictNullChecks": false, 9 | "jsx": "react", 10 | "lib": ["dom", "es6"], 11 | "skipLibCheck": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/Config/UsageTelemetryConfig.ts: -------------------------------------------------------------------------------- 1 | export type UsageTelemetryConfig = { 2 | usageApi: { 3 | headers: { [key: string]: string }; 4 | url: string; 5 | method: string; 6 | resourceId: string; 7 | cache?: 'localStorage'; 8 | }; 9 | enableUpnLogging: false; 10 | sessionDurationMinutes: number; 11 | }; 12 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "sourceMap": true, 8 | "strictNullChecks": false, 9 | "jsx": "react", 10 | "lib": ["dom", "es6"], 11 | "skipLibCheck": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample-react-redux-micro-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@micro-frontend-react/sample-react-redux-micro-frontend", 3 | "private": true, 4 | "version": "1.0.15", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "serve:redux": "webpack-dev-server" 9 | }, 10 | "license": "MIT", 11 | "dependencies": { 12 | "@micro-frontend-react/redux": "^1.0.15" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Buttons/IconButton.ts: -------------------------------------------------------------------------------- 1 | import { ComponentType } from 'react'; 2 | import { IconButton as FabricIconButton } from '@fluentui/react/lib/Button'; 3 | import { withButtonClickLogging } from './withButtonClickLogging'; 4 | import { IButtonProps } from './IButtonProps'; 5 | 6 | export const IconButton = withButtonClickLogging(FabricIconButton as ComponentType); 7 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/global.d.ts: -------------------------------------------------------------------------------- 1 | import * as styledUMD from 'styled-components'; 2 | 3 | export declare global { 4 | const styled: typeof styledUMD.default; 5 | const __IS_DEVELOPMENT__: boolean; 6 | const __APP_NAME__: string; 7 | const __CLIENT_ID__: string; 8 | const __BASE_URL__: string; 9 | const __INSTRUMENTATION_KEY__: string; 10 | const __MSAL__: boolean; 11 | } 12 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Buttons/ActionButton.ts: -------------------------------------------------------------------------------- 1 | import { ComponentType } from 'react'; 2 | import { ActionButton as FabricActionButton } from '@fluentui/react/lib/Button'; 3 | import { withButtonClickLogging } from './withButtonClickLogging'; 4 | import { IButtonProps } from './IButtonProps'; 5 | 6 | export const ActionButton = withButtonClickLogging(FabricActionButton as ComponentType); 7 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Buttons/PrimaryButton.ts: -------------------------------------------------------------------------------- 1 | import { ComponentType } from 'react'; 2 | import { PrimaryButton as FabricPrimaryButton } from '@fluentui/react/lib/Button'; 3 | import { IButtonProps } from './IButtonProps'; 4 | import { withButtonClickLogging } from './withButtonClickLogging'; 5 | 6 | export const PrimaryButton = withButtonClickLogging(FabricPrimaryButton as ComponentType); 7 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Buttons/DefaultButton.ts: -------------------------------------------------------------------------------- 1 | import { ComponentType } from 'react'; 2 | import { DefaultButton as FabricDefaultButton } from '@fluentui/react/lib/Button'; 3 | import { IButtonProps } from './IButtonProps'; 4 | import { withButtonClickLogging } from './withButtonClickLogging'; 5 | 6 | export const DefaultButton = withButtonClickLogging( 7 | FabricDefaultButton as ComponentType 8 | ); 9 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/UsageEvent.ts: -------------------------------------------------------------------------------- 1 | import { UserEvent } from './UsageEvent/UserEvent'; 2 | import { SystemEvent } from './UsageEvent/SytemEvent'; 3 | import { AwareEvent } from './UsageEvent/AwareEvent'; 4 | 5 | export type UsageEvent = UserEvent | SystemEvent | AwareEvent; 6 | 7 | export enum EventType { 8 | User = 'UserAction', 9 | System = 'SystemAction', 10 | Aware = 'AwareAction', 11 | } 12 | -------------------------------------------------------------------------------- /samples/sample-react-redux-micro-frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "sourceMap": true, 8 | "strictNullChecks": false, 9 | "jsx": "react", 10 | "lib": ["dom", "es6"], 11 | "skipLibCheck": true 12 | }, 13 | "include": ["./src", "global.d.ts"] 14 | } 15 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Samples/DynamicSubRoutes/DynamicRouteParamConsumer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as Styled from '../Shared/Layout'; 3 | import { useParams } from 'react-router-dom'; 4 | 5 | export function DynamicRouteParamConsumer(): React.ReactElement { 6 | const { routeParam } = useParams() as { routeParam: string }; 7 | 8 | return Subroute 3 - route param: {routeParam}; 9 | } 10 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/IStoreBuilderResult.ts: -------------------------------------------------------------------------------- 1 | import { Store } from 'redux'; 2 | import { Task, Saga } from 'redux-saga'; 3 | import { IReducerRegistry } from './IReducerRegistry'; 4 | 5 | // eslint-disable-next-line @typescript-eslint/ban-types 6 | export interface IStoreBuilderResult { 7 | store: Store; 8 | reducerRegistry: IReducerRegistry; 9 | runSaga>(saga: S, ...args: Parameters): Task; 10 | context: unknown; 11 | } 12 | -------------------------------------------------------------------------------- /samples/sample-react-host/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@micro-frontend-react/sample-react-host", 3 | "private": true, 4 | "version": "1.0.15", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "serve": "webpack-dev-server" 9 | }, 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "@micro-frontend-react/core": "^1.0.15", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2", 16 | "react-router-dom": "^5.3.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/sample-react-redux-host/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@micro-frontend-react/sample-react-redux-host", 3 | "private": true, 4 | "version": "1.0.15", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "serve:redux": "webpack-dev-server" 9 | }, 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "@micro-frontend-react/redux": "^1.0.15", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2", 16 | "react-router-dom": "^5.3.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Link/Link.utils.ts: -------------------------------------------------------------------------------- 1 | export function shouldUseAnchorTag( 2 | to: string, 3 | forceRefresh: boolean | undefined 4 | ): boolean { 5 | const lowerCasedTo = to.toLowerCase(); 6 | 7 | if (forceRefresh) return true; 8 | if (lowerCasedTo.startsWith('http')) return true; 9 | if (lowerCasedTo.startsWith('mailto')) return true; 10 | if (lowerCasedTo.startsWith('tel')) return true; 11 | if (lowerCasedTo.startsWith('\\')) return true; 12 | 13 | return false; 14 | } 15 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/TextField/TextField.types.ts: -------------------------------------------------------------------------------- 1 | import { ITextFieldProps as IFabricTextFieldProps } from '@fluentui/react/lib/TextField'; 2 | import { UsageFeatureProps } from '../UsageTelemetry'; 3 | 4 | export interface ITextFieldProps extends Pick> { 5 | name: string; 6 | onChange(name: string, value: string): void; 7 | usageEvent: UsageFeatureProps; 8 | logCustomProperties?: () => { 9 | [key: string]: unknown; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Checkbox/Checkbox.types.ts: -------------------------------------------------------------------------------- 1 | import { ICheckboxProps as IFabricCheckboxProps } from '@fluentui/react/lib/Checkbox'; 2 | import { UsageFeatureProps } from '../UsageTelemetry'; 3 | 4 | export interface ICheckboxProps extends Pick> { 5 | name: string; 6 | 7 | onChange(name: string, value: boolean): void; 8 | 9 | usageEvent: UsageFeatureProps; 10 | logCustomProperties?: () => { 11 | [key: string]: unknown; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Dropdown/Dropdown.types.ts: -------------------------------------------------------------------------------- 1 | import { IDropdownProps as IFabricDropdownProps } from '@fluentui/react/lib/Dropdown'; 2 | import { UsageFeatureProps } from '../UsageTelemetry'; 3 | 4 | export interface IDropdownProps extends Pick> { 5 | name: string; 6 | 7 | onChange(name: string, value: string | number): void; 8 | 9 | usageEvent: UsageFeatureProps; 10 | logCustomProperties?: () => { 11 | [key: string]: unknown; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/UsageEvent/AwareEvent.ts: -------------------------------------------------------------------------------- 1 | import { EventType } from '..'; 2 | 3 | export type AwareEvent = { 4 | type: EventType.Aware; 5 | timeTaken: number; 6 | usageUserId: string; 7 | }; 8 | 9 | export const pickAwareEvent = (props: Partial): AwareEvent => { 10 | return (({ timeTaken, usageUserId }): AwareEvent => ({ 11 | type: EventType.Aware, 12 | timeTaken: timeTaken || 0, 13 | usageUserId: usageUserId || 'Error', 14 | }))(props || {}); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/micro-frontend-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "moduleResolution": "node", 5 | "module": "esnext", 6 | "jsx": "react", 7 | "strict": true, 8 | "declaration": true, 9 | "outDir": "./lib", 10 | "sourceMap": true, 11 | "skipLibCheck": true, 12 | "types": ["node"], 13 | "typeRoots": [ 14 | "./node_modules/@types" 15 | ] 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "docs", 20 | ], 21 | "include": [ 22 | "./src" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "moduleResolution": "node", 5 | "module": "esnext", 6 | "jsx": "react", 7 | "strict": true, 8 | "declaration": true, 9 | "outDir": "./lib", 10 | "sourceMap": true, 11 | "skipLibCheck": true, 12 | "types": ["node"], 13 | "typeRoots": [ 14 | "./node_modules/@types" 15 | ] 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "lib" 20 | ], 21 | "include": [ 22 | "./src" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/index.ts: -------------------------------------------------------------------------------- 1 | export * from './UsageFeatureProps'; 2 | export * from './UsageEvent/SytemEvent'; 3 | export * from './UsageEvent/UserEvent'; 4 | export * from './UsageEvent/AwareEvent'; 5 | export * from './Config/UsageTelemetryConfig'; 6 | export * from './UsageTracker'; 7 | export * from './UserAttribute'; 8 | export * from './UsageEvent'; 9 | export * from './UsageLog'; 10 | export * from './Helpers/Usage.helper'; 11 | export * from './Helpers/UsageEventName'; 12 | export * from './Helpers/CuppSchemaMapper'; 13 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/DatePicker/DatePicker.types.ts: -------------------------------------------------------------------------------- 1 | import { IDatePickerProps as IFabricDatePickerProps } from '@fluentui/react/lib/DatePicker'; 2 | import { UsageFeatureProps } from '../UsageTelemetry'; 3 | 4 | export interface IDatePickerProps 5 | extends Pick> { 6 | name: string; 7 | 8 | onChange(name: string, value: Date | null): void; 9 | 10 | usageEvent: UsageFeatureProps; 11 | logCustomProperties?: () => { 12 | [key: string]: unknown; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "moduleResolution": "node", 5 | "module": "esnext", 6 | "jsx": "react", 7 | "strict": true, 8 | "declaration": true, 9 | "outDir": "./lib", 10 | "sourceMap": true, 11 | "skipLibCheck": true, 12 | "types": ["node"], 13 | "typeRoots": [ 14 | "./node_modules/@types", 15 | ] 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "docs", 20 | "lib" 21 | ], 22 | "include": [ 23 | "./src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/IAuthClient.ts: -------------------------------------------------------------------------------- 1 | import { IUser } from './IUser'; 2 | 3 | export interface IAuthClient { 4 | readonly authContext: unknown; 5 | 6 | login(loginOptions?: ILoginOptions): Promise; 7 | 8 | logOut(): Promise; 9 | 10 | getUser(): Promise; 11 | 12 | getUserId(): Promise; 13 | 14 | isLoggedIn(): Promise; 15 | 16 | acquireToken(resourceOrScopes: string | string[]): Promise; 17 | } 18 | 19 | export interface ILoginOptions { 20 | scopes?: string[]; 21 | } 22 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/InjectReduxContext.ts: -------------------------------------------------------------------------------- 1 | import { ReactReduxContext, useSelector } from 'react-redux'; 2 | import { IReduxContext } from './IReduxContext'; 3 | import { IStoreBuilderResult } from './IStoreBuilderResult'; 4 | 5 | export function injectReduxContext(storeBuilderResult: IStoreBuilderResult): IReduxContext { 6 | const { store, ...otherResults } = storeBuilderResult; 7 | 8 | return { 9 | dispatch: store.dispatch, 10 | useSelector, 11 | __redux_context__: ReactReduxContext, 12 | ...otherResults, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/ITelemetryContext.ts: -------------------------------------------------------------------------------- 1 | import { UsageEvent, UserAttribute, UsageTelemetryConfig } from './UsageTelemetry'; 2 | 3 | export interface ITelemetryContext { 4 | sourceComponent: string; 5 | sourceScript: string; 6 | setUsageEvent: (usageEvent: UsageEvent) => UsageEvent; 7 | setUsageUser: (usageUser: UserAttribute) => UserAttribute; 8 | setUsageConfig: (usageConfig: UsageTelemetryConfig) => void; 9 | usageUser: (usageUser: UserAttribute) => UserAttribute; 10 | getChildContext: (appname: string, source: string) => ITelemetryContext; 11 | } 12 | -------------------------------------------------------------------------------- /lage.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | pipeline: { 3 | build: ['^build'], 4 | test: ['build'], 5 | lint: [], 6 | serve: ['^serve'], 7 | 'serve:redux': ['^serve:redux'], 8 | 'serve:ee': ['^serve:ee'], 9 | clean: ['^clean'], 10 | release: ['^release'], 11 | }, 12 | npmClient: 'yarn', 13 | priorities: [ 14 | { 15 | package: '@micro-frontend-react/react', 16 | task: 'build', 17 | priority: 10, 18 | }, 19 | { 20 | package: '@micro-frontend-react/redux', 21 | task: 'build', 22 | priority: 9, 23 | }, 24 | ], 25 | }; 26 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/AuthClient/AuthClient.types.ts: -------------------------------------------------------------------------------- 1 | import { AccountInfo } from '@azure/msal-browser'; 2 | 3 | export interface IAuthClientOptions { 4 | onLogin?(): void; 5 | onLoginFailed?(): void; 6 | onLogout?(): void; 7 | onLogoutFailed?(): void; 8 | onMultipleAccountFound?: (users: AccountInfo[]) => AccountInfo; 9 | onAcquireTokenError?: (e: Error, scopes: string | string[]) => void; 10 | onGetUser?: (token: AccountInfo) => T; 11 | } 12 | 13 | export interface IIDTokenClaim { 14 | family_name?: string; 15 | given_name?: string; 16 | oid?: string; 17 | } 18 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Buttons/withButtonClickLogging.types.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { BaseButton } from '@fluentui/react/lib/Button'; 3 | import { UsageFeatureProps } from '../UsageTelemetry'; 4 | 5 | export interface IWithButtonClickLoggingProps { 6 | onClick?(e: React.MouseEvent): void; 7 | 8 | aiEventName?: string; 9 | text?: string; 10 | title: string; 11 | usageEvent: UsageFeatureProps; 12 | logCustomProperties?: () => { 13 | [key: string]: unknown; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Components/Nav/Nav.types.ts: -------------------------------------------------------------------------------- 1 | import { 2 | INavLinkGroup as IBaseNavLinkGroup, 3 | INavLink as IBaseNavLink, 4 | INavProps as IBaseNavProps, 5 | } from '@coherence-design-system/controls/lib/nav'; 6 | 7 | export interface INavLink extends Omit { 8 | links?: INavLink[]; 9 | } 10 | 11 | export interface INavLinkGroup extends Omit { 12 | links: INavLink[]; 13 | } 14 | 15 | export interface INavProps extends Omit { 16 | appName?: string; 17 | groups: INavLinkGroup[]; 18 | } 19 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Samples/CodeSplitting/MyProfile.tsx: -------------------------------------------------------------------------------- 1 | import { Persona, PersonaSize } from '@micro-frontend-react/employee-experience/lib/Persona'; 2 | import * as React from 'react'; 3 | import { IProfile } from '../Shared/SharedExample.types'; 4 | 5 | export function MyProfile(props: { profile: IProfile }): React.ReactElement { 6 | const { profile } = props; 7 | 8 | return ( 9 |
10 | 11 |
Name: {profile.displayName}
12 |
title: {profile.jobTitle}
13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/navConfig.ts: -------------------------------------------------------------------------------- 1 | export const navConfig = [ 2 | { 3 | key: 'Main', 4 | links: [ 5 | { 6 | key: 1, 7 | name: 'Home', 8 | href: '/', 9 | icon: 'Home', 10 | }, 11 | { 12 | key: 2, 13 | name: 'Dynamic + Sub Routes', 14 | href: '/dynamic-sub-routes', 15 | icon: 'Thunderstorms', 16 | }, 17 | { 18 | key: 3, 19 | name: 'CodeSplitting', 20 | href: '/codesplitting', 21 | icon: 'Thunderstorms', 22 | }, 23 | ], 24 | }, 25 | ]; 26 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/IReduxContext.ts: -------------------------------------------------------------------------------- 1 | import { ReactReduxContext, useSelector } from 'react-redux'; 2 | import { Dispatch } from 'redux'; 3 | import { IReducerRegistry } from './IReducerRegistry'; 4 | import { Saga, Task } from 'redux-saga'; 5 | 6 | export interface IReduxContext { 7 | reducerRegistry: IReducerRegistry; 8 | 9 | runSaga>(saga: S, ...args: Parameters): Task; 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 12 | dispatch: Dispatch; 13 | useSelector: typeof useSelector; 14 | __redux_context__: typeof ReactReduxContext; 15 | } 16 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Samples/Shared/SharedExample.actions.ts: -------------------------------------------------------------------------------- 1 | import { SharedExampleActionType, IRequestProfileAction, IReceiveProfileAction } from './SharedExample.action-types'; 2 | import { IProfile } from './SharedExample.types'; 3 | 4 | export function requestMyProfile(): IRequestProfileAction { 5 | return { 6 | type: SharedExampleActionType.REQUEST_MY_PROFILE 7 | }; 8 | } 9 | 10 | export function receiveFriendByEmail(profile: IProfile): IReceiveProfileAction { 11 | return { 12 | type: SharedExampleActionType.RECEIVE_MY_PROFILE, 13 | profile 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Samples/Shared/SharedExample.action-types.ts: -------------------------------------------------------------------------------- 1 | import { IProfile } from './SharedExample.types'; 2 | 3 | export enum SharedExampleActionType { 4 | REQUEST_MY_PROFILE = 'REQUEST_MY_PROFILE', 5 | RECEIVE_MY_PROFILE = 'RECEIVE_MY_PROFILE', 6 | } 7 | 8 | export type SharedExampleAction = IRequestProfileAction | IReceiveProfileAction; 9 | 10 | export interface IRequestProfileAction { 11 | type: SharedExampleActionType.REQUEST_MY_PROFILE; 12 | } 13 | 14 | export interface IReceiveProfileAction { 15 | type: SharedExampleActionType.RECEIVE_MY_PROFILE; 16 | profile: IProfile; 17 | } 18 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/useUser.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useContext, useState, Context as ReactContext } from 'react'; 2 | import { Context } from '@micro-frontend-react/core/lib/Context'; 3 | import { IEmployeeExperienceContext } from './IEmployeeExperienceContext'; 4 | import { IUser } from './IUser'; 5 | 6 | export function useUser(): IUser | null { 7 | const { authClient } = useContext(Context as ReactContext); 8 | const [user, setUser] = useState(null); 9 | 10 | useEffect(() => { 11 | authClient.getUser().then(setUser); 12 | }, [authClient]); 13 | 14 | return user; 15 | } 16 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/UserAttribute.ts: -------------------------------------------------------------------------------- 1 | import { UsageHelper } from './Helpers/Usage.helper'; 2 | 3 | export type UserAttribute = { 4 | usageUserId: string; 5 | sessionId: string; 6 | lastActiveTime: Date | string; 7 | }; 8 | 9 | export const pickUserAttribute = ( 10 | props: Partial 11 | ): UserAttribute => { 12 | return (({ lastActiveTime, usageUserId, sessionId }): UserAttribute => ({ 13 | lastActiveTime: lastActiveTime || new Date(), 14 | usageUserId: usageUserId || 'Error', 15 | sessionId: sessionId || UsageHelper.Guid(), // TODO: guid 16 | }))(props || {}); 17 | }; 18 | -------------------------------------------------------------------------------- /packages/micro-frontend-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@micro-frontend-react/core", 3 | "version": "1.0.16", 4 | "description": "", 5 | "main": "index.js", 6 | "author": "Microsoft", 7 | "license": "MIT", 8 | "module": "lib/index.js", 9 | "types": "lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "package.json" 13 | ], 14 | "scripts": { 15 | "clean": "rimraf lib", 16 | "build": "tsc -p tsconfig.json", 17 | "release": "npm publish" 18 | }, 19 | "peerDependencies": { 20 | "react": ">= 16 < 19", 21 | "webpack": ">= ^5.69.1 < 6" 22 | }, 23 | "publishConfig": { 24 | "access": "public", 25 | "registry": "https://registry.npmjs.org/" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/RouteComponentProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Route, RouteProps } from 'react-router-dom'; 3 | import { ComponentProvider } from '@micro-frontend-react/core/lib/ComponentProvider'; 4 | import { IComponentProviderProps } from '@micro-frontend-react/core/lib/ComponentProvider/ComponentProvider.types'; 5 | 6 | export function RouteComponentProvider(props: IComponentProviderProps & RouteProps): React.ReactElement { 7 | const { path, config, exact, ...otherProps } = props; 8 | 9 | return ( 10 | } 14 | /> 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/.npmrc: -------------------------------------------------------------------------------- 1 | @ms-ofb:registry=https://microsoftit.pkgs.visualstudio.com/_packaging/EmployeeExperience/npm/registry/ 2 | @microsoft:registry=https://microsoftit.pkgs.visualstudio.com/_packaging/Digital-Workplace/npm/registry/ 3 | @coherence-design-system:registry=https://microsoftit.pkgs.visualstudio.com/OneITVSO/_packaging/SE-DES-Coherence/npm/registry/ 4 | @m365-admin:registry=https://microsoftit.pkgs.visualstudio.com/OneITVSO/_packaging/SE-DES-Coherence/npm/registry/ 5 | @ms:registry=https://microsoftit.pkgs.visualstudio.com/OneITVSO/_packaging/SE-DES-Coherence/npm/registry/ 6 | "registry=https://microsoftit.pkgs.visualstudio.com/OneITVSO/_packaging/OneITVSO_PublicPackages/npm/registry/ 7 | always-auth=true 8 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/yo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@micro-frontend-react/generator-employee-experience", 3 | "version": "1.0.0-alpha.0", 4 | "description": "", 5 | "author": "Microsoft Employee Experience Team", 6 | "license": "UNLICENSED", 7 | "files": [ 8 | "generators" 9 | ], 10 | "devDependencies": { 11 | "husky": "^7.0.4" 12 | }, 13 | "engines": { 14 | "npm": ">= 4.0.0" 15 | }, 16 | "dependencies": { 17 | "chalk": "^5.0.0", 18 | "yeoman-generator": "^5.6.1", 19 | "yosay": "^2.0.2" 20 | }, 21 | "publishConfig": { 22 | "access": "public", 23 | "registry": "https://registry.npmjs.org/" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Samples/Shared/Layout.ts: -------------------------------------------------------------------------------- 1 | import { Text } from '@fluentui/react/lib/Text'; 2 | 3 | export const Container = styled.div` 4 | margin: 48px 5%; 5 | `; 6 | 7 | export const Space = styled.div` 8 | margin-bottom: 24px; 9 | `; 10 | 11 | export const PageHeading = styled(Text).attrs({ 12 | as: 'h1', 13 | variant: 'xLarge', 14 | block: true, 15 | })` 16 | margin-bottom: 6px; 17 | `; 18 | 19 | export const PageDescription = styled(Text).attrs({ 20 | as: 'p', 21 | block: true, 22 | })` 23 | margin-bottom: 24px; 24 | `; 25 | 26 | export const SectionTitle = styled(Text).attrs({ 27 | as: 'h2', 28 | variant: 'large', 29 | block: true, 30 | })` 31 | margin-bottom: 24px; 32 | `; 33 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Samples/Shared/SharedExample.types.ts: -------------------------------------------------------------------------------- 1 | import { IDefaultState } from '@micro-frontend-react/employee-experience/lib/IDefaultState'; 2 | import { sharedExampleReducerName } from './SharedExample.reducer'; 3 | 4 | export interface IExampleAppState extends IDefaultState { 5 | dynamic?: { 6 | [sharedExampleReducerName]: ISharedExampleState; 7 | }; 8 | } 9 | 10 | export interface ISharedExampleState { 11 | profile: IProfile | null; 12 | isLoading: boolean; 13 | hasError: boolean; 14 | errorMessage: string | null; 15 | } 16 | 17 | export interface IProfile { 18 | userPrincipalName: string; 19 | displayName: string; 20 | jobTitle: string; 21 | officeLocation: string; 22 | } 23 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/jest.config.js: -------------------------------------------------------------------------------- 1 | const { jsWithBabel: tsjPreset } = require('ts-jest/presets'); 2 | 3 | module.exports = { 4 | transform: { 5 | ...tsjPreset.transform 6 | }, 7 | collectCoverage: false, 8 | testRegex: '(/__tests__/.*|(\\.|/)(tests))\\.(jsx?|tsx?|js?|ts?)$', 9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 10 | testPathIgnorePatterns: ['node_modules'], 11 | transformIgnorePatterns: ['node_modules/?!(office-ui-fabric-react)'], 12 | moduleNameMapper: {}, 13 | moduleDirectories: ['node_modules', 'src'], 14 | modulePathIgnorePatterns: [], 15 | setupFiles: ['/test.config.ts'], 16 | testResultsProcessor: '/test-result.config.js' 17 | }; 18 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'plugin:@typescript-eslint/recommended', 5 | 'plugin:prettier/recommended', 6 | 'plugin:react/recommended', 7 | 'prettier', 8 | ], 9 | parserOptions: { 10 | ecmaVersion: 2018, 11 | sourceType: 'module', 12 | ecmaFeatures: { 13 | jsx: true, 14 | }, 15 | }, 16 | plugins: ['react-hooks'], 17 | settings: { 18 | react: { 19 | version: 'detect', 20 | }, 21 | }, 22 | rules: { 23 | '@typescript-eslint/interface-name-prefix': 0, 24 | 'react-hooks/rules-of-hooks': 'error', 25 | 'react-hooks/exhaustive-deps': 'warn', 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/IEmployeeExperienceContext.ts: -------------------------------------------------------------------------------- 1 | import { IReduxContext } from '@micro-frontend-react/redux/lib/IReduxContext'; 2 | import { IAuthClient } from './IAuthClient'; 3 | import { IGraphClient } from './IGraphClient'; 4 | import { IHttpClient } from './IHttpClient'; 5 | import { ITelemetryClient } from './ITelemetryClient'; 6 | import { ITelemetryContext } from './ITelemetryContext'; 7 | import { IUsageClient } from './IUsageClient'; 8 | 9 | export interface IEmployeeExperienceContext extends IReduxContext { 10 | authClient: IAuthClient; 11 | httpClient: IHttpClient; 12 | graphClient: IGraphClient; 13 | usageClient: IUsageClient; 14 | telemetryClient: ITelemetryClient; 15 | telemetryContext: ITelemetryContext; 16 | appName?: string; 17 | } 18 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], 4 | parserOptions: { 5 | ecmaVersion: 2018, 6 | sourceType: 'module', 7 | ecmaFeatures: { 8 | jsx: true, 9 | }, 10 | }, 11 | settings: { 12 | react: { 13 | version: 'detect', 14 | }, 15 | }, 16 | plugins: ['react-hooks'], 17 | rules: { 18 | '@typescript-eslint/interface-name-prefix': 0, 19 | '@typescript-eslint/no-empty-interface': 0, 20 | 'react-hooks/rules-of-hooks': 'error', 21 | 'react-hooks/exhaustive-deps': 'warn', 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to Examples template 2 | 3 | This template can be used to create your own Host App, Micro-Frontend App or both at the same time. 4 | To create Micro-Frontend only, please use the My Hub template 5 | 6 | ## Run the template 7 | 8 | 1. Use Node 14 for now due to some peer dependency conflicts 9 | 2. Install Azure Artifiacts tokens: 10 | 11 | If you are on windows: 12 | 13 | 1. run `npm install -g vsts-npm-auth` 14 | 2. run `vsts-npm-auth -config .npmrc` from the root of this template 15 | 16 | If you are on mac: 17 | 18 | 1. Please follow the "Connect to Feed" steps for: 19 | 20 | 1. Framework: https://microsoftit.visualstudio.com/OneITVSO/_packaging?_a=feed&feed=EmployeeExperience 21 | 22 | 3. Run `npm install` 23 | 24 | 4. Run `npm start` to run the Example app 25 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Link/Link.types.ts: -------------------------------------------------------------------------------- 1 | import { UsageFeatureProps } from '../UsageTelemetry'; 2 | import * as React from 'react'; 3 | 4 | export interface ILinkProps extends React.AnchorHTMLAttributes, 5 | Omit, 'type'>, 6 | React.RefAttributes { 7 | to: string; 8 | title: string; 9 | exact?: boolean; 10 | target?: '_blank' | '_self'; 11 | // eslint-disable-next-line @typescript-eslint/ban-types 12 | activeStyle?: {}; 13 | className?: string; 14 | activeClassName?: string; 15 | disabled?: boolean; 16 | refresh?: boolean; 17 | usageEvent: UsageFeatureProps; 18 | logCustomProperties?: () => { 19 | [key: string]: unknown; 20 | }; 21 | ariaLabel: string; 22 | onClick: () => void; 23 | } -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/CodeSplitter/CodeSplitter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ICodeSplitterProps, ICodeSplitterState } from './CodeSplitter.types'; 3 | 4 | export class CodeSplitter extends React.Component, ICodeSplitterState> { 5 | public state: ICodeSplitterState = { 6 | Component: null, 7 | }; 8 | 9 | public async componentDidMount(): Promise { 10 | const { import: asyncImport, name } = this.props; 11 | 12 | const result = await asyncImport(); 13 | this.setState({ 14 | Component: result[name], 15 | }); 16 | } 17 | 18 | public render(): JSX.Element | null { 19 | const { props } = this.props; 20 | const Component: React.ComponentType | null = this.state.Component; 21 | if (!Component) return null; 22 | 23 | return ; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/useDynamicReducer.ts: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect, useContext, Context as ReactContext } from 'react'; 2 | import { Reducer } from 'redux'; 3 | import { Saga } from 'redux-saga'; 4 | import { Context } from '@micro-frontend-react/core/lib/Context'; 5 | import { IReduxContext } from './IReduxContext'; 6 | 7 | export function useDynamicReducer( 8 | reducerName: string, 9 | reducer: Reducer, 10 | sagas: Saga[] = [], 11 | shouldPersist = true 12 | ): void { 13 | const { reducerRegistry, runSaga } = useContext(Context as ReactContext); 14 | 15 | useLayoutEffect(() => { 16 | if (!reducerRegistry.exists(reducerName)) { 17 | const skipIfExists = true; 18 | reducerRegistry.registerDynamic(reducerName, reducer, skipIfExists, shouldPersist); 19 | 20 | sagas.map((saga) => runSaga(saga)); 21 | } 22 | }, [reducer, reducerName, reducerRegistry, runSaga, sagas, shouldPersist]); 23 | } 24 | -------------------------------------------------------------------------------- /samples/sample-react-micro-frontend/webpack.config.js: -------------------------------------------------------------------------------- 1 | const generateBuildConfig = require('@micro-frontend-react/core/lib/WebpackConfigs'); 2 | 3 | const devServerPort = 8000; 4 | const useHttps = false; 5 | const openBrowser = false; 6 | 7 | const globalVariables = { 8 | /* ========================= 9 | * Add build time variables here 10 | * These variables also need to added in ./global.d.ts file to be available in Typescript 11 | * ====================== */ 12 | 13 | __APP_NAME__: 'Micro-Frontend Application', 14 | }; 15 | 16 | const microFrontendEntries = { 17 | /* ========================= 18 | * Add Webpack entry for host application 19 | * ====================== */ 20 | 21 | 'micro-frontend-app': './src/MicroFrontendApp.tsx', 22 | }; 23 | 24 | module.exports = generateBuildConfig({ 25 | cwd: __dirname, 26 | microFrontendEntries, 27 | devServer: { 28 | devServerPort, 29 | useHttps, 30 | openBrowser, 31 | }, 32 | globalVariables, 33 | }); 34 | -------------------------------------------------------------------------------- /samples/sample-react-redux-micro-frontend/webpack.config.js: -------------------------------------------------------------------------------- 1 | const generateBuildConfig = require('@micro-frontend-react/core/lib/WebpackConfigs'); 2 | 3 | const devServerPort = 8000; 4 | const useHttps = false; 5 | const openBrowser = false; 6 | 7 | const globalVariables = { 8 | /* ========================= 9 | * Add build time variables here 10 | * These variables also need to added in ./global.d.ts file to be available in Typescript 11 | * ====================== */ 12 | 13 | __APP_NAME__: 'Micro-Frontend Application', 14 | }; 15 | 16 | const microFrontendEntries = { 17 | /* ========================= 18 | * Add Webpack entry for host application 19 | * ====================== */ 20 | 21 | 'micro-frontend-app': './src/MicroFrontendApp.tsx', 22 | }; 23 | 24 | module.exports = generateBuildConfig({ 25 | cwd: __dirname, 26 | microFrontendEntries, 27 | devServer: { 28 | devServerPort, 29 | useHttps, 30 | openBrowser, 31 | }, 32 | globalVariables, 33 | }); 34 | -------------------------------------------------------------------------------- /samples/sample-react-micro-frontend/src/MicroFrontendApp.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Context, withContext } from '@micro-frontend-react/core/lib/Context'; 3 | 4 | function MicroFrontendApp(): React.ReactElement { 5 | const { userProvider, customData } = React.useContext( 6 | Context as React.Context<{ 7 | userProvider: { getUserName(): string }; 8 | customData?: string; 9 | }> 10 | ); 11 | const [userName, setUserName] = React.useState('Guest'); 12 | 13 | React.useEffect(() => { 14 | const userName = userProvider.getUserName(); 15 | setUserName(userName); 16 | }, [userProvider]); 17 | 18 | return ( 19 |
24 | Hello, {userName} from {__APP_NAME__} 25 | {customData ? ` with ${customData}` : ''}! 26 |
27 | ); 28 | } 29 | 30 | const connected = withContext(MicroFrontendApp); 31 | export { connected as MicroFrontendApp }; 32 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/yo/generators/app/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const Generator = require("yeoman-generator"); 4 | const chalk = require("chalk"); 5 | const yosay = require("yosay"); 6 | const pkg = require("../../package.json"); 7 | 8 | module.exports = class extends Generator { 9 | async prompting() { 10 | this.log( 11 | yosay( 12 | `Welcome to the Microsoft ${chalk.green( 13 | `Employee Experience Framework v${pkg.version}` 14 | )} generator!` 15 | ) 16 | ); 17 | 18 | writing() 19 | { 20 | this.fs.copy(this.templatePath("examples/**/*"), this.destinationRoot()); 21 | this.fs.copy(this.templatePath("examples/**/.*"), this.destinationRoot()); 22 | } 23 | 24 | install() 25 | { 26 | this.log("\nSuccess! Follow the steps in README.md to install dependencies and run the app"); 27 | } 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /samples/sample-react-redux-host/src/Home.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ComponentProvider } from '@micro-frontend-react/core/lib/ComponentProvider'; 3 | import { Context } from '@micro-frontend-react/core/lib/Context'; 4 | import { IReduxContext } from '@micro-frontend-react/redux/lib/IReduxContext'; 5 | import { AppState } from './App'; 6 | 7 | export function Home(): React.ReactElement { 8 | const { useSelector } = React.useContext(Context as React.Context); 9 | 10 | const hostAppName = useSelector((appState: AppState) => appState.host.hostAppName); 11 | 12 | return ( 13 | <> 14 |
19 | Hello from {hostAppName} 20 |
21 | 22 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Routes.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Switch } from 'react-router-dom'; 3 | import { RouteComponentProvider } from '@micro-frontend-react/employee-experience/lib/RouteComponentProvider'; 4 | 5 | export function Routes(): React.ReactElement { 6 | return ( 7 | 8 | 16 | 23 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Shell/Shell.styled.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components'; 2 | 3 | export const ShellStyles = createGlobalStyle` 4 | *, *:before, *:after { 5 | box-sizing: border-box; 6 | } 7 | 8 | html { 9 | height: 100%; 10 | } 11 | 12 | body { 13 | position: relative; 14 | height: 100%; 15 | margin: 0; 16 | color: #333; 17 | background-color: #F2F2F2; 18 | overflow-x: hidden; 19 | } 20 | 21 | h1, h2, h3, h4, p, ul { 22 | margin: 0; 23 | } 24 | 25 | button { 26 | cursor: pointer; 27 | } 28 | 29 | a { 30 | cursor: pointer; 31 | text-decoration: none; 32 | } 33 | 34 | ul { 35 | list-style: none; 36 | } 37 | 38 | body > iframe { 39 | display: none; 40 | } 41 | 42 | #app { 43 | height: 100%; 44 | 45 | & > div { 46 | height: 100%; 47 | } 48 | } 49 | 50 | @keyframes placeholderBlinks { 51 | 0% { 52 | opacity: 1; 53 | } 54 | 50% { 55 | opacity: 0.7; 56 | } 57 | } 58 | `; 59 | -------------------------------------------------------------------------------- /packages/micro-frontend-react/src/Context.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Context as ReactContext, createContext } from 'react'; 3 | import { IComponentProps } from './IComponentProps'; 4 | 5 | export const Context: ReactContext = createContext({}); 6 | 7 | export function withContext(WrappedComponent: React.ComponentType): React.ComponentType { 8 | const ComponentWithContext: React.ComponentType = (props: IComponentProps): React.ReactElement => { 9 | const { context, config, ...otherProps } = props; 10 | 11 | // Having no config means that this component is not a micro-frontend, 12 | // and there is no need to re-initialize the Context 13 | if (!config) return ; 14 | 15 | ComponentWithContext.displayName = config.name; 16 | 17 | return ( 18 | 19 | 20 | 21 | ); 22 | }; 23 | 24 | return ComponentWithContext as React.ComponentType; 25 | } 26 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/src/IReducerRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ReducersMapObject, AnyAction } from 'redux'; 2 | 3 | export interface IReducerRegistry { 4 | getReducers(): ReducersMapObject; 5 | 6 | getDynamicReducers(): ReducersMapObject; 7 | 8 | exists(reducerName: string): boolean; 9 | 10 | register( 11 | name: string, 12 | reducer: (state: S, action: A) => S, 13 | skipIfExists?: boolean 14 | ): IReducerRegistry; 15 | 16 | registerDynamic( 17 | name: string, 18 | reducer: (state: S, action: A) => S, 19 | skipIfExists?: boolean, 20 | shouldPersist?: boolean 21 | ): IReducerRegistry; 22 | 23 | addChangeListener(name: string, fn: ReducerChangeListener): IReducerRegistry; 24 | 25 | removeChangeListener(name: string): IReducerRegistry; 26 | } 27 | 28 | export type ReducerNames = string[]; 29 | 30 | export type ReducerChangeListener = ( 31 | reducers: ReducersMapObject, 32 | dynamicReducers: ReducersMapObject, 33 | blacklistedDynamicReducer: ReducerNames 34 | ) => void; 35 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@micro-frontend-react/employee-experience-generator", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "", 6 | "scripts": { 7 | "yo:install": "npm --prefix ./yo install ./yo", 8 | "yo:clean": "npx rimraf yo/generators/app/templates/examples", 9 | "yo:copy": "npx copyfiles -a \"./examples/**/*\" \"./yo/generators/app/templates\" -e \"./examples/node_modules/**/*\" -e \"./examples/public/bundles/**/*\" -e \"./examples/public/ocv/**/*\"", 10 | "yo:build": "npm run yo:clean && npm run yo:copy && npm --prefix ./yo link ./yo", 11 | "examples:install": "npm --prefix ./examples install ./examples", 12 | "examples:serve": "npm --prefix ./examples start ./examples", 13 | "nuke": "npx rimraf **/node_modules **/package-lock.json" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://microsoftit.visualstudio.com/DefaultCollection/OneITVSO/_git/HR-HRX-EE-UXFrwk-Generator" 18 | }, 19 | "author": "", 20 | "license": "MIT License" 21 | } 22 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@micro-frontend-react/employee-experience", 3 | "version": "1.0.16", 4 | "description": "Micro-Frontend React Extension for Microsoft Employee Experience Team", 5 | "main": "index.js", 6 | "author": "Microsoft", 7 | "license": "MIT", 8 | "module": "lib/index.js", 9 | "types": "lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "package.json" 13 | ], 14 | "scripts": { 15 | "clean": "rimraf lib", 16 | "build": "tsc -p tsconfig.json", 17 | "release": "npm publish" 18 | }, 19 | "dependencies": { 20 | "@micro-frontend-react/redux": "^1.0.15" 21 | }, 22 | "peerDependencies": { 23 | "@azure/msal-browser": ">= ^2.22.0 < 3", 24 | "@fluentui/react": ">= ^8.56.1 < 9", 25 | "@microsoft/applicationinsights-web": ">= ^2.7.3 < 3", 26 | "axios": ">= ^1.0 < 2.0", 27 | "styled-components": ">= ^5.3.0 < 6", 28 | "uuid": ">= ^9.0.0 < 10" 29 | }, 30 | "publishConfig": { 31 | "access": "public", 32 | "registry": "https://registry.npmjs.org/" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/micro-frontend-react-redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@micro-frontend-react/redux", 3 | "version": "1.0.16", 4 | "description": "", 5 | "main": "index.js", 6 | "author": "Microsoft", 7 | "license": "MIT", 8 | "module": "lib/index.js", 9 | "types": "lib/index.d.ts", 10 | "files": [ 11 | "lib", 12 | "package.json" 13 | ], 14 | "scripts": { 15 | "clean": "rimraf lib", 16 | "build": "tsc -p tsconfig.json", 17 | "release": "npm publish" 18 | }, 19 | "dependencies": { 20 | "@micro-frontend-react/core": "^1.0.15" 21 | }, 22 | "peerDependencies": { 23 | "@redux-devtools/extension": ">= ^3.2.2 < 4", 24 | "react": ">= 16 < 19", 25 | "react-redux": ">= ^8.0.0-rc.0 < 9", 26 | "react-router-dom": ">= ^5.3.0 < 6", 27 | "redux": ">= ^4.1.2 < 5", 28 | "redux-logger": ">= ^3.0.6 < 4", 29 | "redux-persist": ">= ^6.0.0 < 7", 30 | "redux-saga": ">= ^1.1.3 < 2", 31 | "webpack": ">= ^5.69.1 < 6" 32 | }, 33 | "publishConfig": { 34 | "access": "public", 35 | "registry": "https://registry.npmjs.org/" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/useGraphPhoto.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useContext, useState, Context as ReactContext } from 'react'; 2 | import { Context } from '@micro-frontend-react/core/lib/Context'; 3 | import { IEmployeeExperienceContext } from './IEmployeeExperienceContext'; 4 | import { GraphPhotoSize } from './IGraphClient'; 5 | 6 | export function useGraphPhoto(upn?: string, size?: GraphPhotoSize): string | null { 7 | const { graphClient, authClient } = useContext(Context as ReactContext); 8 | const [photo, setPhoto] = useState(null); 9 | 10 | useEffect(() => { 11 | if (upn) { 12 | graphClient.getPhoto(upn, size).then(setPhoto); 13 | 14 | return; 15 | } 16 | 17 | authClient 18 | .getUserId() 19 | .then((userId: string | null): void => { 20 | if (userId) graphClient.getPhoto(userId).then(setPhoto); 21 | else setPhoto(null); 22 | }) 23 | .catch(() => { 24 | setPhoto(null); 25 | }); 26 | }, [authClient, graphClient, upn]); 27 | 28 | return photo; 29 | } 30 | -------------------------------------------------------------------------------- /samples/sample-react-host/src/Home.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Context } from '@micro-frontend-react/core/lib/Context'; 3 | import { ComponentProvider } from '@micro-frontend-react/core/lib/ComponentProvider'; 4 | 5 | export function Home(): React.ReactElement { 6 | const { userProvider } = React.useContext( 7 | Context as React.Context<{ 8 | userProvider: { getUserName(): string }; 9 | }> 10 | ); 11 | const [userName, setUserName] = React.useState('Guest'); 12 | 13 | React.useEffect(() => { 14 | const userName = userProvider.getUserName(); 15 | setUserName(userName); 16 | }, [userProvider]); 17 | 18 | return ( 19 | <> 20 |
25 | Hello, {userName} from {__APP_NAME__} 26 |
27 | 28 | 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/UsageEvent/UserEvent.ts: -------------------------------------------------------------------------------- 1 | import { EventType } from '..'; 2 | 3 | export type UserEvent = { 4 | feature?: string; 5 | subFeature?: string; 6 | subFeatureLevel2?: string; 7 | featureLocation?: string; 8 | type: EventType.User; 9 | businessTransactionId?: string; 10 | eventName: string; 11 | experienceResult?: boolean; 12 | timeTaken?: number; 13 | }; 14 | 15 | export const pickUserEvent = (props: Partial): UserEvent => { 16 | return (({ 17 | businessTransactionId, 18 | eventName, 19 | experienceResult, 20 | feature, 21 | subFeature, 22 | subFeatureLevel2, 23 | featureLocation, 24 | timeTaken, 25 | }): UserEvent => ({ 26 | type: EventType.User, 27 | businessTransactionId, 28 | eventName: eventName as string, 29 | experienceResult: experienceResult || true, 30 | feature, 31 | subFeature, 32 | subFeatureLevel2, 33 | featureLocation, 34 | timeTaken: timeTaken || 0, 35 | }))(props || {}); 36 | }; 37 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/UsageEvent/SytemEvent.ts: -------------------------------------------------------------------------------- 1 | import { EventType } from '../UsageEvent'; 2 | 3 | export type SystemEvent = { 4 | eventName?: string; 5 | feature?: string; 6 | subFeature?: string; 7 | subFeatureLevel2?: string; 8 | featureLocation?: string; 9 | timeTaken: number; 10 | type: EventType.System; 11 | businessTransactionId?: string; 12 | experienceResult?: boolean; 13 | }; 14 | 15 | export const pickSystemEvent = (props: Partial): SystemEvent => { 16 | return (({ 17 | eventName, 18 | feature, 19 | subFeature, 20 | subFeatureLevel2, 21 | featureLocation, 22 | timeTaken, 23 | businessTransactionId, 24 | experienceResult, 25 | }): SystemEvent => ({ 26 | type: EventType.System, 27 | eventName, 28 | feature, 29 | subFeature, 30 | subFeatureLevel2, 31 | featureLocation, 32 | timeTaken: timeTaken || 0, 33 | experienceResult: experienceResult || true, 34 | businessTransactionId, 35 | }))(props || {}); 36 | }; 37 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Samples/Shared/SharedExample.sagas.ts: -------------------------------------------------------------------------------- 1 | import { SimpleEffect, getContext, put, all, call, takeLatest } from 'redux-saga/effects'; 2 | import { IHttpClient, IHttpClientResult } from '@micro-frontend-react/employee-experience/lib/IHttpClient'; 3 | import { SharedExampleActionType } from './SharedExample.action-types'; 4 | import { receiveFriendByEmail } from './SharedExample.actions'; 5 | import { IProfile } from './SharedExample.types'; 6 | 7 | const graphBaseUrl = 'https://graph.microsoft.com/v1.0/'; 8 | const graphResourceUri = 'https://graph.microsoft.com'; 9 | 10 | function* fetchProfile(): IterableIterator> { 11 | const httpClient: IHttpClient = yield getContext('httpClient'); 12 | const { data }: IHttpClientResult = yield call([httpClient, httpClient.request], { 13 | url: `${graphBaseUrl}/me`, 14 | resource: graphResourceUri, 15 | }); 16 | 17 | yield put(receiveFriendByEmail(data)); 18 | } 19 | 20 | export function* sharedExampleSagas(): IterableIterator<{}> { 21 | yield all([takeLatest(SharedExampleActionType.REQUEST_MY_PROFILE, fetchProfile)]); 22 | } 23 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const { jsWithBabel: tsjPreset } = require('ts-jest/presets'); 2 | 3 | module.exports = { 4 | transform: { 5 | ...tsjPreset.transform, 6 | }, 7 | testRegex: '(/__tests__/.*|(\\.|/)(tests))\\.(jsx?|tsx?|js?|ts?)$', 8 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 9 | testPathIgnorePatterns: ['node_modules'], 10 | transformIgnorePatterns: ['node_modules/?!(@fluentui/react)'], 11 | moduleNameMapper: {}, 12 | moduleDirectories: ['node_modules', 'src'], 13 | modulePathIgnorePatterns: [], 14 | snapshotSerializers: ['enzyme-to-json/serializer'], 15 | setupFiles: ['/test.config.ts'], 16 | testResultsProcessor: '/test-result.config.js', 17 | coverageReporters: ['cobertura', 'text'], 18 | collectCoverage: true, 19 | collectCoverageFrom: [ 20 | '**/*.{ts,tsx}', 21 | '!**/lib/**', 22 | '!**/*.types.ts', 23 | '!**/*.action-types.ts', 24 | '!**/*.styled.ts', 25 | '!**/*.actions.ts', 26 | '!**/index.ts', 27 | ], 28 | coverageThreshold: { 29 | global: { 30 | branches: 30, 31 | functions: 30, 32 | lines: 30, 33 | statements: 30, 34 | }, 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /samples/sample-react-host/webpack.config.js: -------------------------------------------------------------------------------- 1 | const generateBuildConfig = require('@micro-frontend-react/core/lib/WebpackConfigs'); 2 | 3 | const devServerPort = 9000; 4 | const useHttps = false; 5 | const openBrowser = true; 6 | 7 | const globalVariables = { 8 | /* ========================= 9 | * Add build time variables here 10 | * These variables also need to added in ./global.d.ts file to be available in Typescript 11 | * ====================== */ 12 | 13 | __APP_NAME__: 'Micro-Frontend Host Application', 14 | }; 15 | 16 | const hostEntries = { 17 | /* ========================= 18 | * Add Webpack entry for host application 19 | * ====================== */ 20 | 21 | app: './src/App.tsx', 22 | }; 23 | 24 | module.exports = generateBuildConfig({ 25 | cwd: __dirname, 26 | hostEntries, 27 | devServer: { 28 | devServerPort, 29 | useHttps, 30 | openBrowser, 31 | }, 32 | globalVariables, 33 | externalScripts: [ 34 | 'https://ee.azureedge.net/react/17.0.2/react.production.min.js', 35 | 'https://ee.azureedge.net/react/17.0.2/react-is.production.min.js', 36 | 'https://ee.azureedge.net/react/17.0.2/react-dom.production.min.js', 37 | ], 38 | }); 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /packages/micro-frontend-react/src/ComponentProvider/ComponentProvider.types.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { IComponentProps } from '../IComponentProps'; 3 | import { IComponentConfig } from '../IComponentConfig'; 4 | 5 | export interface IDuplicateRequestHandler { 6 | resolve: (template: React.ComponentType) => void; 7 | reject: (error: Error) => void; 8 | } 9 | 10 | export interface IDuplicateRequestHandlers { 11 | [key: string]: IDuplicateRequestHandler[]; 12 | } 13 | 14 | export interface IComponentProviderProps { 15 | config: IComponentConfig; 16 | data?: unknown; 17 | 18 | onLoad?(config: IComponentConfig): void; 19 | 20 | onLoaded?(config: IComponentConfig): void; 21 | 22 | onError?(error: Error): void; 23 | 24 | renderError?(): React.ReactElement; 25 | 26 | renderPlaceholder?(): React.ReactElement; 27 | } 28 | 29 | export interface IComponentProviderState { 30 | Component: React.ComponentType | null; 31 | hasError: boolean; 32 | } 33 | 34 | declare global { 35 | interface Window { 36 | __MICRO_FRONTENDS__: { 37 | [key: string]: { 38 | [key: string]: React.ComponentType; 39 | }; 40 | }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /samples/sample-react-host/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { BrowserRouter, Route } from 'react-router-dom'; 4 | import { ComponentProvider } from '@micro-frontend-react/core/lib/ComponentProvider'; 5 | import { Context } from '@micro-frontend-react/core/lib/Context'; 6 | import { Home } from './Home'; 7 | 8 | const userProvider = { 9 | getUserName: () => 'Sample User', 10 | }; 11 | export function App(): React.ReactElement { 12 | return ( 13 | 14 | 15 | ( 18 | 27 | )} 28 | /> 29 | 30 | ); 31 | } 32 | 33 | render( 34 | 39 | 40 | , 41 | document.getElementById('app') 42 | ); 43 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Persona/Persona.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Persona as FabricPersona } from '@fluentui/react/lib/Persona'; 3 | import { GraphPhotoSize } from '../IGraphClient'; 4 | import { IPersonaProps, PersonaSize } from './Persona.types'; 5 | import { useGraphPhoto } from '../useGraphPhoto'; 6 | 7 | export const Persona: React.FC = (props: IPersonaProps): React.ReactElement => { 8 | const { emailAlias } = props; 9 | const photo = useGraphPhoto(emailAlias, getPixelSize(props.size)); 10 | 11 | if (!photo) return ; 12 | 13 | return ; 14 | }; 15 | 16 | const getPixelSize = (size: PersonaSize | undefined): GraphPhotoSize => { 17 | switch (size) { 18 | case PersonaSize.size8: 19 | case PersonaSize.size24: 20 | case PersonaSize.size32: 21 | case PersonaSize.size40: 22 | case PersonaSize.size48: 23 | return 48; 24 | case PersonaSize.size56: 25 | return 64; 26 | case PersonaSize.size72: 27 | return 96; 28 | case PersonaSize.size100: 29 | case PersonaSize.size120: 30 | return 120; 31 | default: 32 | return undefined; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/Samples/Shared/SharedExample.reducer.ts: -------------------------------------------------------------------------------- 1 | import { ISharedExampleState } from './SharedExample.types'; 2 | import { SharedExampleAction, SharedExampleActionType } from './SharedExample.action-types'; 3 | 4 | export const sharedExampleReducerName = 'SharedExampleReducer'; 5 | export const sharedExampleInitialState: ISharedExampleState = { 6 | profile: null, 7 | isLoading: false, 8 | hasError: false, 9 | errorMessage: null 10 | }; 11 | 12 | export function sharedExampleReducer( 13 | prev: ISharedExampleState = sharedExampleInitialState, 14 | action: SharedExampleAction 15 | ): ISharedExampleState { 16 | switch (action.type) { 17 | case SharedExampleActionType.REQUEST_MY_PROFILE: 18 | return { 19 | ...prev, 20 | isLoading: true, 21 | hasError: false 22 | }; 23 | 24 | case SharedExampleActionType.RECEIVE_MY_PROFILE: 25 | return { 26 | ...prev, 27 | isLoading: false, 28 | hasError: false, 29 | profile: action.profile 30 | }; 31 | default: 32 | return prev; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Checkbox/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Checkbox as FabricCheckbox } from '@fluentui/react/lib/Checkbox'; 3 | import { IEmployeeExperienceContext } from '../IEmployeeExperienceContext'; 4 | import { ICheckboxProps } from './Checkbox.types'; 5 | import { Context } from '../Context'; 6 | import { UserEvent, UsageEventName, EventType } from '../UsageTelemetry'; 7 | 8 | export function Checkbox(props: ICheckboxProps): React.ReactElement { 9 | const { onChange, name, usageEvent } = props; 10 | const { telemetryClient } = React.useContext(Context as React.Context); 11 | 12 | const handleChanged = ( 13 | e: React.FormEvent | undefined, 14 | value: boolean | undefined 15 | ): void => { 16 | const checkedEvent: UserEvent = { 17 | eventName: UsageEventName.CheckBoxChanged, 18 | type: EventType.User, 19 | businessTransactionId: value?.toString() || 'null', 20 | ...usageEvent, 21 | }; 22 | 23 | const customProps = props.logCustomProperties?.() || {}; 24 | telemetryClient.trackEvent(checkedEvent, customProps); 25 | 26 | onChange(name, value as boolean); 27 | }; 28 | 29 | return ; 30 | } 31 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/withStore.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { IStoreBuilderResult } from '@micro-frontend-react/redux/lib/IStoreBuilderResult'; 4 | import { injectReduxContext } from '@micro-frontend-react/redux/lib/InjectReduxContext'; 5 | import { IEmployeeExperienceContext } from './IEmployeeExperienceContext'; 6 | 7 | export function withStore( 8 | storeBuilderResult: IStoreBuilderResult 9 | ): (WrappedComponent: React.ComponentType) => React.ComponentType { 10 | const { store, context, ...otherResults } = storeBuilderResult; 11 | 12 | return (WrappedComponent: React.ComponentType): React.ComponentType => { 13 | const ComponentWithStore: React.ComponentType = (props: React.PropsWithChildren): React.ReactElement => { 14 | const { children } = props; 15 | 16 | const childProps = { 17 | ...(context as IEmployeeExperienceContext), 18 | ...otherResults, 19 | ...injectReduxContext(storeBuilderResult), 20 | }; 21 | 22 | return ( 23 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | return ComponentWithStore; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /samples/sample-react-redux-host/webpack.config.js: -------------------------------------------------------------------------------- 1 | const generateBuildConfig = require('@micro-frontend-react/core/lib/WebpackConfigs'); 2 | 3 | const devServerPort = 9000; 4 | const useHttps = false; 5 | const openBrowser = true; 6 | 7 | const globalVariables = { 8 | /* ========================= 9 | * Add build time variables here 10 | * These variables also need to added in ./global.d.ts file to be available in Typescript 11 | * ====================== */ 12 | 13 | __IS_DEVELOPMENT__: process.env.NODE_ENV === 'development' || true, 14 | __APP_NAME__: 'Micro-Frontend Host Application', 15 | __BASE_URL__: process.env.baseUrl || `http${useHttps ? 's' : ''}://localhost:${devServerPort}`, 16 | }; 17 | 18 | const hostEntries = { 19 | /* ========================= 20 | * Add Webpack entry for host application 21 | * ====================== */ 22 | 23 | app: './src/App.tsx', 24 | }; 25 | 26 | module.exports = generateBuildConfig({ 27 | cwd: __dirname, 28 | hostEntries, 29 | devServer: { 30 | devServerPort, 31 | useHttps, 32 | openBrowser, 33 | }, 34 | globalVariables, 35 | externalScripts: [ 36 | 'https://ee.azureedge.net/react/17.0.2/react.production.min.js', 37 | 'https://ee.azureedge.net/react/17.0.2/react-is.production.min.js', 38 | 'https://ee.azureedge.net/react/17.0.2/react-dom.production.min.js', 39 | ], 40 | }); 41 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # TODO: The maintainer of this repo has not yet edited this file 2 | 3 | **REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? 4 | 5 | - **No CSS support:** Fill out this template with information about how to file issues and get help. 6 | - **Yes CSS support:** Fill out an intake form at [aka.ms/spot](https://aka.ms/spot). CSS will work with/help you to determine next steps. More details also available at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). 7 | - **Not sure?** Fill out a SPOT intake as though the answer were "Yes". CSS will help you decide. 8 | 9 | *Then remove this first heading from this SUPPORT.MD file before publishing your repo.* 10 | 11 | # Support 12 | 13 | ## How to file issues and get help 14 | 15 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 16 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 17 | feature request as a new Issue. 18 | 19 | For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE 20 | FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER 21 | CHANNEL. WHERE WILL YOU HELP PEOPLE?**. 22 | 23 | ## Microsoft Support Policy 24 | 25 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 26 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/useUsageTelemetry.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useContext, Context as ReactContext } from 'react'; 2 | import { Context } from '@micro-frontend-react/core/lib/Context'; 3 | import { UserAttribute, pickUserAttribute } from './UsageTelemetry'; 4 | import { IEmployeeExperienceContext } from './IEmployeeExperienceContext'; 5 | import { useUser } from './useUser'; 6 | 7 | export function useUsageTelemetry(): void { 8 | const { usageClient, telemetryContext } = useContext(Context as ReactContext); 9 | const user = useUser(); 10 | const initializeUsage = async (): Promise => { 11 | const userParams = telemetryContext?.usageUser || ({} as UserAttribute); 12 | if (usageClient) { 13 | telemetryContext?.setUsageConfig(usageClient?.getUsageConfig()); 14 | } 15 | if (user && usageClient) { 16 | //After the user object is initialized, we can request the usage apis 17 | const usageUserId = await usageClient?.getUsageUserId(); 18 | const usageUser = pickUserAttribute({ 19 | ...userParams, 20 | usageUserId, 21 | }); 22 | telemetryContext?.setUsageUser(usageUser); 23 | } 24 | }; 25 | 26 | useEffect(() => { 27 | initializeUsage(); 28 | // eslint-disable-next-line 29 | }, [user, usageClient]); // Prettier introduces the local initializeUsage which re-renders always 30 | } 31 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/Helpers/CuppSchemaMapper.ts: -------------------------------------------------------------------------------- 1 | import { CustomProperties } from '../../ITelemetryClient'; 2 | 3 | const CuppSchema: { [key: string]: string } = { 4 | type: 'UsageEventType', 5 | timeTaken: 'UsageTimeTaken', 6 | eventName: 'UsageEventName', 7 | feature: 'UsageCapabilityName', 8 | subFeature: 'UsageSubCapabilityName', 9 | subFeatureLevel2: 'UsageSubCapabilityLevel2', 10 | featureLocation: 'UsageLocation', 11 | businessTransactionId: 'UsageBusinessTransactionId', 12 | experienceResult: 'UsageExperienceResult', 13 | usageUserId: 'UsageUserId', 14 | sessionId: 'UsageSessionId', // TOOD: ras1 => UsageSessionTrackingId ? 15 | actionTrackingId: 'UsageActionTrackingId', 16 | correlationTrackingId: 'UsagecCorrelationTrackingId', 17 | pageTrackingId: 'UsagePageTrackingId', 18 | eventDate: 'UsageEventDate', 19 | flightId: 'UsageFlightId', 20 | flightName: 'UsageFlightName', 21 | moduleName: 'UsageModuleName', 22 | usageVersion: 'UsageVersion', 23 | }; 24 | 25 | export const asCuppSchema = (props: CustomProperties): CustomProperties => 26 | Object.keys(props).reduce( 27 | (acc, key: string) => ({ 28 | ...acc, 29 | ...{ 30 | [CuppSchema[key] || key]: 31 | props[key] instanceof Date ? (props[key] as Date).toISOString() : props[key] !== undefined ? props[key] : '', 32 | }, 33 | }), 34 | {} 35 | ); 36 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/useHeaderConfig.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ICoherenceHeaderProps } from '@coherence-design-system/controls/lib/header'; 3 | 4 | export function useHeaderConfig(): ICoherenceHeaderProps { 5 | return { 6 | headerLabel: __APP_NAME__, 7 | appNameSettings: { 8 | label: __APP_NAME__, 9 | }, 10 | searchSettings: { 11 | placeholder: 'Search...', 12 | onSearch: (term: string): void => alert(term), 13 | }, 14 | farRightSettings: { 15 | notificationsSettings: { 16 | panelSettings: { 17 | items: [], 18 | newNotifications: 1, 19 | }, 20 | }, 21 | feedbackSettings: { 22 | panelSettings: { 23 | ocvButtonIsFocused: false, 24 | onClick: () => { 25 | return true; 26 | }, 27 | launchOptions: { 28 | categories: { 29 | show: true, 30 | customCategories: ['Dashboard', 'Feed', 'Catalog', 'Vision', 'Hearing', 'Mobility', 'Cognitive'], 31 | }, 32 | }, 33 | }, 34 | }, 35 | settingsSettings: { 36 | panelSettings: { 37 | body: <>Settings Contents, 38 | }, 39 | }, 40 | helpSettings: { 41 | panelSettings: { 42 | body: <>Help Contents, 43 | }, 44 | }, 45 | }, 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/Shell/Shell.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Context } from '@micro-frontend-react/core/lib/Context'; 3 | import { ITelemetryContext } from '../ITelemetryContext'; 4 | import { UsageHelper, UserAttribute } from '../UsageTelemetry'; 5 | import { ShellStyles } from './Shell.styled'; 6 | import { IEmployeeExperienceContext } from '../IEmployeeExperienceContext'; 7 | 8 | export function Shell(props: React.PropsWithChildren): React.ReactElement { 9 | const { children, ...context } = props; 10 | const { appName } = context; 11 | 12 | const usageHelper = appName && appName.trim() != '' ? UsageHelper.Fork(appName) : UsageHelper; 13 | const telemetryContext: ITelemetryContext = { 14 | sourceComponent: 'Shell', 15 | sourceScript: 'main', 16 | setUsageEvent: usageHelper.MassageEvent, 17 | setUsageUser: (usageUser: UserAttribute) => { 18 | UsageHelper.SetUser(usageUser); 19 | usageHelper.SetUser(usageUser); 20 | return usageUser; 21 | }, 22 | usageUser: usageHelper.GetUser, 23 | setUsageConfig: usageHelper.SetUsageConfig, 24 | getChildContext: usageHelper.ForkTelemetryContext, 25 | }; 26 | context.telemetryClient?.setContext(telemetryContext); 27 | 28 | return ( 29 | <> 30 | 31 | {children} 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # OS specific 64 | .DS_Store 65 | 66 | # Build artifacts 67 | lib/ 68 | debug/ 69 | bin/ 70 | dist/ 71 | bundles/ 72 | public/index.html 73 | buildonce/ 74 | 75 | # editor specific 76 | .IDEA/ 77 | .vs/ 78 | -------------------------------------------------------------------------------- /samples/sample-react-redux-micro-frontend/src/SampleMicroFrontendReduxStore.ts: -------------------------------------------------------------------------------- 1 | import { SagaIterator } from 'redux-saga'; 2 | import { getContext, put, all, takeLatest, call } from 'redux-saga/effects'; 3 | 4 | export type SampleUser = { 5 | name: string; 6 | email: string; 7 | }; 8 | 9 | export type MicroFrontendState = { 10 | user?: SampleUser; 11 | }; 12 | 13 | export enum MicroFrontendActionType { 14 | 'REQUEST_USER' = 'REQUEST_USER', 15 | 'RECEIVE_USER' = 'RECEIVE_USER', 16 | } 17 | 18 | type MicroFrontendAction = 19 | | { 20 | type: MicroFrontendActionType.REQUEST_USER; 21 | } 22 | | { 23 | type: MicroFrontendActionType.RECEIVE_USER; 24 | user: SampleUser; 25 | }; 26 | 27 | export function microFrontendReducer(prev: MicroFrontendState = {}, action: MicroFrontendAction) { 28 | switch (action.type) { 29 | case MicroFrontendActionType.RECEIVE_USER: 30 | return { 31 | user: action.user, 32 | }; 33 | default: 34 | return prev; 35 | } 36 | } 37 | 38 | function* fetchUser(): SagaIterator { 39 | const httpClient = yield getContext('httpClient'); 40 | 41 | const user = yield call([httpClient, httpClient.get], 'http://localhost:9000/api/user.json'); 42 | 43 | yield put({ 44 | type: MicroFrontendActionType.RECEIVE_USER, 45 | user, 46 | }); 47 | } 48 | 49 | export function* microFrontendSagas(): SagaIterator { 50 | yield all([takeLatest(MicroFrontendActionType.REQUEST_USER, fetchUser)]); 51 | } 52 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/TelemetryEvents.ts: -------------------------------------------------------------------------------- 1 | // These telemetries were used while back when this library had no support for HEART metrics 2 | // Since then the event names have been replaced with UsageTelemetry event names 3 | // These are provided for backward compatibility if you've been using them in your projects 4 | export enum TelemetryEvents { 5 | // App 6 | SessionStarted = 'SessionStarted', 7 | 8 | // User 9 | UserLogInRequested = 'UserLogInRequested', 10 | UserLoginFailed = 'UserLoginFailed', 11 | UserLogOutRequested = 'UserLogOutRequested', 12 | UserLogOutFailed = 'UserLogOutFailed', 13 | AcquireTokenFailed = 'AcquireTokenFailed', 14 | 15 | // Routes 16 | PageEnter = 'PageEnter', 17 | PageLeave = 'PageLeave', 18 | 19 | // Side nav 20 | NavLinkClicked = 'NavLinkClicked', 21 | 22 | // Header 23 | HeaderAppNameLinkClicked = 'HeaderAppNameLinkClicked', 24 | HeaderSearchRequested = 'HeaderSearchRequested', 25 | HeaderPanelOpened = 'HeaderPanelOpened', 26 | HeaderPanelClosed = 'HeaderPanelClosed', 27 | 28 | InvalidComponentConfig = 'InvalidComponentConfig', 29 | 30 | // HTTP 31 | APIRequestStarted = 'APIRequestStarted', 32 | APIResponseReceived = 'APIResponseReceived', 33 | APIFailedWithoutResponse = 'APIFailedWithoutResponse', 34 | APIFailedResponseReceived = 'APIFailedResponseReceived', 35 | 36 | // Components 37 | ButtonClicked = 'ButtonClicked', 38 | LinkClicked = 'LinkClicked', 39 | FormElementClicked = 'FormElementClicked', 40 | } 41 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/useLoginOnStartup.ts: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect, useState, Context as ReactContext } from 'react'; 2 | import { Context } from '@micro-frontend-react/core/lib/Context'; 3 | import { IUser } from './IUser'; 4 | import { ILoginOptions } from './IAuthClient'; 5 | import { IEmployeeExperienceContext } from './IEmployeeExperienceContext'; 6 | 7 | export function useLoginOnStartup(shouldLogin?: boolean, options: ILoginOptions = {}): [IUser | null] { 8 | const { authClient, telemetryClient } = useContext(Context as ReactContext); 9 | const [user, setUser] = useState(null); 10 | 11 | useEffect(() => { 12 | telemetryClient.trackTrace({ message: 'SessionStarted' }); 13 | 14 | if (user) return telemetryClient.setAuthenticatedUserContext(user.id); 15 | }, []); 16 | 17 | useEffect(() => { 18 | if (shouldLogin === false) return; 19 | if (user) return telemetryClient.setAuthenticatedUserContext(user.id); 20 | 21 | authClient 22 | .isLoggedIn() 23 | .then(async (isLoggedIn): Promise => { 24 | if (isLoggedIn) { 25 | const loggedInUser = await authClient.getUser(); 26 | setUser(loggedInUser); 27 | 28 | return; 29 | } 30 | 31 | authClient.login(options).catch(); 32 | }) 33 | .catch(() => { 34 | authClient.login(options).catch(); 35 | }); 36 | }, [authClient, shouldLogin, telemetryClient, user]); 37 | 38 | return [user]; 39 | } 40 | -------------------------------------------------------------------------------- /extensions/microsoft-employee-experience/src/UsageTelemetry/UsageTracker.ts: -------------------------------------------------------------------------------- 1 | import { EventType } from './UsageEvent'; 2 | 3 | export type UsageTracker = { 4 | name?: string; 5 | actionTrackingId?: string; 6 | correlationTrackingId?: string; 7 | pageTrackingId?: string; 8 | startTime?: Date; 9 | eventDate?: Date; 10 | type: string; 11 | flightId?: string; // TODO: Move this to a different action => Flighting Action 12 | flightName?: string; // TODO: Move this to a different action => Flighting Action 13 | moduleName?: string; 14 | usageUserId?: string; 15 | usageVersion?: string; 16 | inheritedName?: string; 17 | }; 18 | 19 | export const pickUsageTracker = ( 20 | props: Partial 21 | ): UsageTracker => { 22 | return (({ 23 | name, 24 | actionTrackingId, 25 | correlationTrackingId, 26 | pageTrackingId, 27 | startTime, 28 | eventDate, 29 | type, 30 | flightId, 31 | flightName, 32 | moduleName, 33 | usageUserId, 34 | }): UsageTracker => ({ 35 | name, 36 | actionTrackingId, 37 | correlationTrackingId, 38 | pageTrackingId, 39 | startTime: startTime || new Date(), 40 | eventDate: eventDate || new Date(), 41 | type: type || EventType.User, // Default the type to user when the type is not passed in 42 | flightId, 43 | flightName, 44 | moduleName, 45 | usageUserId, 46 | }))(props || {}); 47 | }; 48 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # OS specific 64 | .DS_Store 65 | 66 | # Build artifacts 67 | lib/ 68 | !**/__mocks__/**/lib 69 | debug/ 70 | bin/ 71 | bundles/ 72 | public/ocv/ 73 | public/index.html 74 | public/buildonce/ 75 | 76 | # editor specific 77 | .IDEA/ 78 | .vs/ 79 | .swp 80 | 81 | # test results 82 | test-results.trx 83 | 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # OS specific 64 | .DS_Store 65 | 66 | # Build artifacts 67 | lib/ 68 | !**/__mocks__/**/lib 69 | debug/ 70 | bin/ 71 | bundles/ 72 | **/public/index.html 73 | 74 | # editor specific 75 | .idea 76 | .vs/ 77 | .swp 78 | 79 | 80 | # test results 81 | test-results.trx 82 | 83 | # App specific 84 | generator/generators/app/templates/examples 85 | generator/generators/app/templates/myhub 86 | yarn.lock 87 | -------------------------------------------------------------------------------- /generators/microsoft-employee-experience-generator/examples/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { CoherenceTheme } from '@coherence-design-system/styles'; 2 | import * as React from 'react'; 3 | import { render } from 'react-dom'; 4 | import { BrowserRouter } from 'react-router-dom'; 5 | import { initializeOBFeedback } from '@coherence-design-system/controls'; 6 | import { useLoginOnStartup } from '@micro-frontend-react/employee-experience/lib/useLoginOnStartup'; 7 | import { Header } from './Components/Header'; 8 | import { Nav } from './Components/Nav'; 9 | import { Main } from './Components/Main'; 10 | import { ShellWithStore } from './ShellWithStore'; 11 | import { navConfig } from './navConfig'; 12 | import { Routes } from './Routes'; 13 | import { useHeaderConfig } from './useHeaderConfig'; 14 | import { loadTheme, ThemeProvider } from '@fluentui/react'; 15 | 16 | initializeOBFeedback( 17 | 1111, 18 | 'running-environment', 19 | '/ocv/OfficeBrowserFeedback.min.js', 20 | '/ocv/OfficeBrowserFeedback.min.css', 21 | '/ocv/intl/', 22 | 'https://office365.uservoice.com/' 23 | ); 24 | 25 | function App(): React.ReactElement { 26 | useLoginOnStartup(); 27 | loadTheme(CoherenceTheme); 28 | const headerConfig = useHeaderConfig(); 29 | 30 | return ( 31 | 32 | 33 |
34 |