feat: Initialize organization chart web part with core functionality and configuration
- Added configuration files: config.json, copy-assets.json, package-solution.json, serve.json, write-manifests.json. - Created gulpfile.js for build process. - Implemented core logic in OrganizationChartCore.ts for account normalization and profile handling. - Developed data service in OrganizationChartDataService.ts to fetch organization data from SharePoint User Profile Service. - Defined types for user profiles and organization nodes in OrganizationChartTypes.ts. - Created web part manifest for OrganizationChartWebPart. - Built the main web part component with rendering logic and property pane configuration in OrganizationChartWebPart.ts. - Developed React components for displaying the organization chart. - Implemented styling for the organization chart in OrganizationChart.module.scss. - Added localization support with English and German strings. - Created unit tests for core functionalities in OrganizationChartCore.test.ts. - Set up TypeScript and TSLint configurations for the project.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
normalizeAccount,
|
||||
normalizeCacheMinutes,
|
||||
normalizeMaxDepth,
|
||||
normalizeMaxPeople,
|
||||
normalizeProfileUrl,
|
||||
profileProperty,
|
||||
uniqueAccounts
|
||||
} from '../src/webparts/organizationChart/OrganizationChartCore';
|
||||
import { IUserProfileProperty } from '../src/webparts/organizationChart/OrganizationChartTypes';
|
||||
|
||||
declare const console: { log(message: string): void };
|
||||
let passed: number = 0;
|
||||
function assert(condition: boolean, message: string): void { if (!condition) { throw new Error(message); } passed++; }
|
||||
|
||||
const properties: IUserProfileProperty[] = [
|
||||
{ Key: 'Department', Value: 'IT' },
|
||||
{ Key: 'Manager', Value: 'CONTOSO\\manager' }
|
||||
];
|
||||
assert(normalizeAccount(' CONTOSO\\user ') === 'CONTOSO\\user', 'Account must be trimmed.');
|
||||
assert(profileProperty(properties, 'department') === 'IT', 'Profile-property lookup must ignore casing.');
|
||||
assert(profileProperty(properties, 'Office') === '', 'Missing profile properties must be empty.');
|
||||
assert(uniqueAccounts(['A', 'a', 'B', 'C'], 'B', 10).join(',') === 'A,C', 'Accounts must be unique and excludable.');
|
||||
assert(normalizeMaxDepth(0) === 6 && normalizeMaxDepth(20) === 10, 'Tree depth must be constrained.');
|
||||
assert(normalizeMaxPeople(1) === 10 && normalizeMaxPeople(900) === 500, 'Person count must be constrained.');
|
||||
assert(normalizeCacheMinutes(-1) === 0 && normalizeCacheMinutes(300) === 120, 'Cache duration must be constrained.');
|
||||
assert(normalizeProfileUrl('javascript:alert(1)', 'http://portal') === '', 'Unsafe profile URLs must be rejected.');
|
||||
assert(normalizeProfileUrl('/my/person.aspx', 'http://portal') === '/my/person.aspx', 'Server-relative URLs must be retained.');
|
||||
|
||||
console.log('Organization chart core tests passed: ' + passed);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "../temp/tests",
|
||||
"rootDir": "..",
|
||||
"lib": ["es5", "dom", "es2015.promise"],
|
||||
"types": [],
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"files": [
|
||||
"../src/webparts/organizationChart/OrganizationChartTypes.ts",
|
||||
"../src/webparts/organizationChart/OrganizationChartCore.ts",
|
||||
"OrganizationChartCore.test.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var root = path.resolve(__dirname, '..');
|
||||
var passed = 0;
|
||||
function read(file) { return fs.readFileSync(path.join(root, file), 'utf8'); }
|
||||
function json(file) { return JSON.parse(read(file)); }
|
||||
function assert(condition, message) { if (!condition) { throw new Error(message); } passed++; }
|
||||
|
||||
var packageJson = json('package.json');
|
||||
var packageSolution = json('config/package-solution.json');
|
||||
var config = json('config/config.json');
|
||||
var manifest = json('src/webparts/organizationChart/OrganizationChartWebPart.manifest.json');
|
||||
var service = read('src/webparts/organizationChart/OrganizationChartDataService.ts');
|
||||
var webPart = read('src/webparts/organizationChart/OrganizationChartWebPart.ts');
|
||||
var component = read('src/webparts/organizationChart/components/OrganizationChart.tsx');
|
||||
var styles = read('src/webparts/organizationChart/components/OrganizationChart.module.scss');
|
||||
|
||||
assert(packageJson.name === 'organization-chart-webpart', 'Package name is inconsistent.');
|
||||
assert(packageSolution.solution.name === packageJson.name && packageSolution.solution.title === packageJson.name,
|
||||
'Solution name is inconsistent.');
|
||||
assert(packageSolution.paths.zippedPackage === 'solution/organization-chart-webpart.sppkg', 'Package file name is inconsistent.');
|
||||
assert(packageSolution.solution.includeClientSideAssets === true && packageSolution.solution.skipFeatureDeployment === true,
|
||||
'Central deployment is not configured.');
|
||||
assert(!!config.bundles['organization-chart-webpart'], 'Bundle name is inconsistent.');
|
||||
assert(manifest.componentType === 'WebPart' && manifest.alias === 'OrganizationChartWebPart', 'Manifest is invalid.');
|
||||
assert(service.indexOf('/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor') >= 0, 'User Profile REST access is missing.');
|
||||
assert(service.indexOf('profile.directReports') >= 0 && service.indexOf('this.buildNode(childAccount, depth + 1') >= 0,
|
||||
'Recursive downward organization loading is missing.');
|
||||
assert(webPart.indexOf("PropertyPaneTextField('startUser'") >= 0, 'Starting-person configuration is missing.');
|
||||
assert(webPart.indexOf("PropertyPaneSlider('maxDepth'") >= 0 && webPart.indexOf("PropertyPaneSlider('maxPeople'") >= 0,
|
||||
'Tree safety limits are missing.');
|
||||
assert(component.indexOf("role='tree'") >= 0 && component.indexOf("role='treeitem'") >= 0,
|
||||
'Accessible tree semantics are missing.');
|
||||
assert(component.indexOf('aria-expanded') >= 0, 'Accessible expand/collapse behavior is missing.');
|
||||
['bodyText', 'neutralSecondary', 'neutralLighter', 'themePrimary', 'neutralTertiary', 'white', 'neutralLight', 'focusBorder', 'errorText']
|
||||
.forEach(function(slot) {
|
||||
assert(styles.indexOf('[theme:' + slot + ', default:') >= 0, 'SharePoint theme slot is missing: ' + slot);
|
||||
});
|
||||
|
||||
console.log('Organization Chart project validation passed: ' + passed);
|
||||
Reference in New Issue
Block a user