diff --git a/README.md b/README.md index 25bc667..988b51f 100644 --- a/README.md +++ b/README.md @@ -511,6 +511,28 @@ The `AuditManagement` widget will let you embed an audit table in your site. Example: [Manage Audit](./projects/demo-app/src/app/manage-audit/manage-audit.component.html) +#### UserProfile + +The `UserProfile` widget lets you embed a user profile component in your app and let the logged in user update his profile. + +The widget lets you: + +- Update user profile picture +- Update user personal information +- Update authentication methods +- Logout + +###### Usage + +```angular2html + +``` + +Example: +[My User Profile](./projects/demo-app/src/app/my-user-profile/my-user-profile.component.html) + ## Code Example You can find an example angular app in the [examples folder](./projects/demo-app). diff --git a/package-lock.json b/package-lock.json index 64f07a2..000cd06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,12 @@ "version": "0.5.5", "license": "MIT", "dependencies": { - "@descope/access-key-management-widget": "0.1.35", - "@descope/audit-management-widget": "0.1.0", - "@descope/role-management-widget": "0.1.35", - "@descope/user-management-widget": "0.4.36", - "@descope/web-component": "3.8.39", + "@descope/access-key-management-widget": "0.1.51", + "@descope/audit-management-widget": "0.1.14", + "@descope/role-management-widget": "0.1.49", + "@descope/user-management-widget": "0.4.51", + "@descope/user-profile-widget": "0.0.15", + "@descope/web-component": "3.11.9", "tslib": "^2.3.0" }, "devDependencies": { @@ -2549,14 +2550,14 @@ } }, "node_modules/@descope/access-key-management-widget": { - "version": "0.1.35", - "resolved": "https://registry.npmjs.org/@descope/access-key-management-widget/-/access-key-management-widget-0.1.35.tgz", - "integrity": "sha512-UFF7pCBNKq9O9yggHkpXq83tgcukSSRreFRig8ybViNy61ourSdpdLIc8tXESEiix67DngbBS/bD15cN90aq9A==", - "dependencies": { - "@descope/sdk-component-drivers": "0.2.0", - "@descope/sdk-helpers": "0.1.19", - "@descope/sdk-mixins": "0.2.0", - "@descope/web-js-sdk": "1.10.31", + "version": "0.1.51", + "resolved": "https://registry.npmjs.org/@descope/access-key-management-widget/-/access-key-management-widget-0.1.51.tgz", + "integrity": "sha512-zVcNtWN8LHSLy7ZrkGVDbZpp5x+q+EkCHm7cY3pRfzF/4ocu1juGJm16smFHNHzSMMh0rgvdlrTsTG9djGCFnA==", + "dependencies": { + "@descope/sdk-component-drivers": "0.2.7", + "@descope/sdk-helpers": "0.1.26", + "@descope/sdk-mixins": "0.2.8", + "@descope/web-js-sdk": "1.10.41", "@reduxjs/toolkit": "^2.0.1", "immer": "^10.0.3", "redux": "5.0.1", @@ -2566,14 +2567,14 @@ } }, "node_modules/@descope/audit-management-widget": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@descope/audit-management-widget/-/audit-management-widget-0.1.0.tgz", - "integrity": "sha512-hz4qRKW+9redujE/uoNLRjCsgLRXanBmlPL0Z3wl8Xum9/uUycRR8BfY6zjxUYrgjPJNYscqZ+q2kJ3DOMPSiQ==", - "dependencies": { - "@descope/sdk-component-drivers": "0.2.0", - "@descope/sdk-helpers": "0.1.19", - "@descope/sdk-mixins": "0.2.0", - "@descope/web-js-sdk": "1.10.31", + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/@descope/audit-management-widget/-/audit-management-widget-0.1.14.tgz", + "integrity": "sha512-x1yXEGws3MGl0Xex8M8hy0RtZxPWJ2vc2Fspahh8ZE0o9CNrs1yNeZm3URxx7cny4chQBIIP9Xf1MXMg/VPOkw==", + "dependencies": { + "@descope/sdk-component-drivers": "0.2.7", + "@descope/sdk-helpers": "0.1.26", + "@descope/sdk-mixins": "0.2.8", + "@descope/web-js-sdk": "1.10.41", "@reduxjs/toolkit": "^2.0.1", "immer": "^10.0.3", "redux": "5.0.1", @@ -2583,22 +2584,22 @@ } }, "node_modules/@descope/core-js-sdk": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/@descope/core-js-sdk/-/core-js-sdk-2.12.1.tgz", - "integrity": "sha512-NwoGjcBHAgb9r7Xx4o90GtN8XeXdvpVyadKia2FJyUbIaqtcsaA4IOi2h+Ei6c0L+e36aJW1NavaTb3W9rDHMw==", + "version": "2.14.4", + "resolved": "https://registry.npmjs.org/@descope/core-js-sdk/-/core-js-sdk-2.14.4.tgz", + "integrity": "sha512-lHcBdTEAWMcWsAGxG9F+Pny74mUSC0dpChEZpPlQ0+DdT5+ERP+Wcx3yVg/bUo3Fl1ldKfEDN/XBrWaa4BNQ+w==", "dependencies": { "jwt-decode": "3.1.2" } }, "node_modules/@descope/role-management-widget": { - "version": "0.1.35", - "resolved": "https://registry.npmjs.org/@descope/role-management-widget/-/role-management-widget-0.1.35.tgz", - "integrity": "sha512-nTCTl0Kn0VeOQZxPuOaNUPR5aXIwolazWs2F1fPI6bs5yxUU4rKVoSefsPkiMY1I0CzMwwH9KfXUNfmn4AIU6A==", - "dependencies": { - "@descope/sdk-component-drivers": "0.2.0", - "@descope/sdk-helpers": "0.1.19", - "@descope/sdk-mixins": "0.2.0", - "@descope/web-js-sdk": "1.10.31", + "version": "0.1.49", + "resolved": "https://registry.npmjs.org/@descope/role-management-widget/-/role-management-widget-0.1.49.tgz", + "integrity": "sha512-cEfZaSF9Zh0S1N3SDOzsIVk8UZnDphSFAt/Df9iLE9CM90xGcGGDkqME8FBj4wOvmpbTeSM4XsJ4YzETXoBEVQ==", + "dependencies": { + "@descope/sdk-component-drivers": "0.2.7", + "@descope/sdk-helpers": "0.1.26", + "@descope/sdk-mixins": "0.2.8", + "@descope/web-js-sdk": "1.10.41", "@reduxjs/toolkit": "^2.0.1", "immer": "^10.0.3", "redux": "5.0.1", @@ -2608,29 +2609,29 @@ } }, "node_modules/@descope/sdk-component-drivers": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@descope/sdk-component-drivers/-/sdk-component-drivers-0.2.0.tgz", - "integrity": "sha512-bfbBeweAmXo9DJ3/CgVdSL3zpqwGWHugMddpeZ1MdRObLjkZr3K7hXWSkTVv4vPndO+ODoF1YgHv2U0iFqIw1A==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@descope/sdk-component-drivers/-/sdk-component-drivers-0.2.7.tgz", + "integrity": "sha512-pHvOqnZYeLRElw4mm+ejNq2zPBo9i/J5eaA3y3iTYfRlbKWUyVC5G740CRIFrl3vmNDQc1mTx+7A8kwsZrXWgQ==", "dependencies": { - "@descope/sdk-helpers": "0.1.19", + "@descope/sdk-helpers": "0.1.26", "tslib": "2.6.2" } }, "node_modules/@descope/sdk-helpers": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@descope/sdk-helpers/-/sdk-helpers-0.1.19.tgz", - "integrity": "sha512-Y14I2lFOg6cl0uCU6UQ0I6Nh+lfxu9xdlTV3a8IhbzxmRBkyvopQ+AyZSo0kolJJ0CJujxAjx74dA6w9AfgxGw==", + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/@descope/sdk-helpers/-/sdk-helpers-0.1.26.tgz", + "integrity": "sha512-k/ggo4utkb91OoI9sBTYbDMoOB47RgsGkFsAbR2pz6yJRy6y05Y2ql6wyecN7qBCBsvAXfjsiZLpHhdrxj/Kog==", "dependencies": { "tslib": "2.6.2" } }, "node_modules/@descope/sdk-mixins": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@descope/sdk-mixins/-/sdk-mixins-0.2.0.tgz", - "integrity": "sha512-cQA19PxXjtqEEoHTu2oteKW2aOPXOyOpSGbavkuAzYYdfFSnBGPivJ+S6w8/8jgUeIL8PZ+9qO7fU3D5x/k4/Q==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@descope/sdk-mixins/-/sdk-mixins-0.2.8.tgz", + "integrity": "sha512-r3fwt2aKOPznE/YRqChlZGW3yP8q6eR1bpVIBSxg3QAriUGG0q+VK4NsjlzGH/hj+gcm0Do0cP4JXZFjVAcl8g==", "dependencies": { - "@descope/sdk-component-drivers": "0.2.0", - "@descope/sdk-helpers": "0.1.19", + "@descope/sdk-component-drivers": "0.2.7", + "@descope/sdk-helpers": "0.1.26", "tslib": "2.6.2" }, "peerDependencies": { @@ -2641,14 +2642,38 @@ } }, "node_modules/@descope/user-management-widget": { - "version": "0.4.36", - "resolved": "https://registry.npmjs.org/@descope/user-management-widget/-/user-management-widget-0.4.36.tgz", - "integrity": "sha512-QpKmz7F7NPbTSnGvmZoUGB0WHIQWglmrykU+h3GS4g2Xmlh8ymN4dCLZtkOChh3xlQyvDeFT/jSHuvw2TNSf1A==", - "dependencies": { - "@descope/sdk-component-drivers": "0.2.0", - "@descope/sdk-helpers": "0.1.19", - "@descope/sdk-mixins": "0.2.0", - "@descope/web-js-sdk": "1.10.31", + "version": "0.4.51", + "resolved": "https://registry.npmjs.org/@descope/user-management-widget/-/user-management-widget-0.4.51.tgz", + "integrity": "sha512-YZ5PhEE3E5Hh700SN8nWtPUnYbOBS0uJQ0otcBp1XdTHUisVZP1TmYF/bWAibPfL4FOd0iCL13V9sRjo67zaug==", + "dependencies": { + "@descope/sdk-component-drivers": "0.2.7", + "@descope/sdk-helpers": "0.1.26", + "@descope/sdk-mixins": "0.2.8", + "@descope/web-js-sdk": "1.10.41", + "@reduxjs/toolkit": "^2.0.1", + "immer": "^10.0.3", + "libphonenumber-js": "1.10.59", + "redux": "5.0.1", + "redux-thunk": "3.1.0", + "reselect": "5.1.0", + "tslib": "2.6.2" + } + }, + "node_modules/@descope/user-management-widget/node_modules/libphonenumber-js": { + "version": "1.10.59", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.59.tgz", + "integrity": "sha512-HeTsOrDF/hWhEiKqZVwg9Cqlep5x2T+IYDENvT2VRj3iX8JQ7Y+omENv+AIn0vC8m6GYhivfCed5Cgfw27r5SA==" + }, + "node_modules/@descope/user-profile-widget": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@descope/user-profile-widget/-/user-profile-widget-0.0.15.tgz", + "integrity": "sha512-b0rb0+Y8ZERrvJmPKsGFY0lUH7YrCBWOHULzL7U/2DpevVgnNkR15XHwcc/gWCGb5U+JvX5XMcwLECKj124mfA==", + "dependencies": { + "@descope/sdk-component-drivers": "0.2.7", + "@descope/sdk-helpers": "0.1.26", + "@descope/sdk-mixins": "0.2.8", + "@descope/web-component": "3.11.9", + "@descope/web-js-sdk": "1.10.41", "@reduxjs/toolkit": "^2.0.1", "immer": "^10.0.3", "libphonenumber-js": "1.10.58", @@ -2656,24 +2681,27 @@ "redux-thunk": "3.1.0", "reselect": "5.1.0", "tslib": "2.6.2" + }, + "optionalDependencies": { + "@descope/core-js-sdk": "2.14.4" } }, "node_modules/@descope/web-component": { - "version": "3.8.39", - "resolved": "https://registry.npmjs.org/@descope/web-component/-/web-component-3.8.39.tgz", - "integrity": "sha512-L02gnsrk4+MvH6kRSo69npOI1a61FNr0/IqWzcxUfJ/In0j4+t5i34vvjLQlv00AIP2hIUv/Vses5vlLXvUF+g==", + "version": "3.11.9", + "resolved": "https://registry.npmjs.org/@descope/web-component/-/web-component-3.11.9.tgz", + "integrity": "sha512-NhuWnmsis5YJxE2ajuoNc42qiGzte9l+nYFPG/jhCMBtzi5ZkDmY0GGCmjrjfDFv58Zq7iKbXlQ5WjXDsMzdTg==", "dependencies": { - "@descope/web-js-sdk": "1.10.31", + "@descope/web-js-sdk": "1.10.41", "tslib": "2.6.2" } }, "node_modules/@descope/web-js-sdk": { - "version": "1.10.31", - "resolved": "https://registry.npmjs.org/@descope/web-js-sdk/-/web-js-sdk-1.10.31.tgz", - "integrity": "sha512-xzznemDCNXT8TjKmiEGddFsXUxt3Y9CfOdpcQGkeFKdu7OJPAWlyLyxPk/s4mClTtHJ/ebwOfbhV26mBg10tQQ==", + "version": "1.10.41", + "resolved": "https://registry.npmjs.org/@descope/web-js-sdk/-/web-js-sdk-1.10.41.tgz", + "integrity": "sha512-c27Cyt8V1cxoe0JCUIfWKF/lqiZvEKfUUwdMZWIdP+Kzq082NYRe3exdWn1lu7N4dZ/hBFEGAuW4yVRs3SSgiA==", "dependencies": { - "@descope/core-js-sdk": "2.12.1", - "@fingerprintjs/fingerprintjs-pro": "3.9.2", + "@descope/core-js-sdk": "2.14.4", + "@fingerprintjs/fingerprintjs-pro": "3.9.3", "js-cookie": "3.0.5", "tslib": "2.6.2" } @@ -3163,9 +3191,9 @@ } }, "node_modules/@fingerprintjs/fingerprintjs-pro": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs-pro/-/fingerprintjs-pro-3.9.2.tgz", - "integrity": "sha512-H8CkfBp8LAfH8m7QU/BWvn+wBew6PWwN7SpChw7nTQfuDDzd7RtUGmF/O1b00Url6/MmGRsOM6bjZUrH8N49BA==", + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs-pro/-/fingerprintjs-pro-3.9.3.tgz", + "integrity": "sha512-Hm/7IQiJGGjCA6zDs34YOwtNjr1PueftdX6cR24Blh479HDcZedAlVslaQlV8aZcBdvlIlKxyqkWNQwHTXVahQ==", "dependencies": { "tslib": "^2.4.1" } diff --git a/package.json b/package.json index 8bfb59e..bde15a1 100644 --- a/package.json +++ b/package.json @@ -33,11 +33,12 @@ ] }, "dependencies": { - "@descope/access-key-management-widget": "0.1.35", - "@descope/audit-management-widget": "0.1.0", - "@descope/role-management-widget": "0.1.35", - "@descope/user-management-widget": "0.4.36", - "@descope/web-component": "3.8.39", + "@descope/access-key-management-widget": "0.1.51", + "@descope/audit-management-widget": "0.1.14", + "@descope/role-management-widget": "0.1.49", + "@descope/user-management-widget": "0.4.51", + "@descope/user-profile-widget": "0.0.15", + "@descope/web-component": "3.11.9", "tslib": "^2.3.0" }, "optionalDependencies": { diff --git a/projects/angular-sdk/ng-package.json b/projects/angular-sdk/ng-package.json index 9620a46..a1946ab 100644 --- a/projects/angular-sdk/ng-package.json +++ b/projects/angular-sdk/ng-package.json @@ -9,6 +9,7 @@ "@descope/user-management-widget", "@descope/role-management-widget", "@descope/access-key-management-widget", - "@descope/audit-management-widget" + "@descope/audit-management-widget", + "@descope/user-profile-widget" ] } diff --git a/projects/angular-sdk/src/lib/components/access-key-management/access-key-management.component.spec.ts b/projects/angular-sdk/src/lib/components/access-key-management/access-key-management.component.spec.ts index d33a2a1..c137dd3 100644 --- a/projects/angular-sdk/src/lib/components/access-key-management/access-key-management.component.spec.ts +++ b/projects/angular-sdk/src/lib/components/access-key-management/access-key-management.component.spec.ts @@ -51,7 +51,12 @@ describe('DescopeAccessKeyManagementComponent', () => { component.projectId = '123'; component.tenant = 'tenant-1'; component.widgetId = 'widget-1'; - component.logger = { info: jest.fn(), error: jest.fn(), warn: jest.fn() }; + component.logger = { + info: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + debug: jest.fn() + }; fixture.detectChanges(); }); diff --git a/projects/angular-sdk/src/lib/components/audit-management/audit-management.component.spec.ts b/projects/angular-sdk/src/lib/components/audit-management/audit-management.component.spec.ts index 2f9ebc4..d403fde 100644 --- a/projects/angular-sdk/src/lib/components/audit-management/audit-management.component.spec.ts +++ b/projects/angular-sdk/src/lib/components/audit-management/audit-management.component.spec.ts @@ -51,7 +51,12 @@ describe('DescopeAuditManagementComponent', () => { component.projectId = '123'; component.tenant = 'tenant-1'; component.widgetId = 'widget-1'; - component.logger = { info: jest.fn(), error: jest.fn(), warn: jest.fn() }; + component.logger = { + info: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + debug: jest.fn() + }; fixture.detectChanges(); }); diff --git a/projects/angular-sdk/src/lib/components/descope/descope.component.spec.ts b/projects/angular-sdk/src/lib/components/descope/descope.component.spec.ts index 64bec75..d45c6ca 100644 --- a/projects/angular-sdk/src/lib/components/descope/descope.component.spec.ts +++ b/projects/angular-sdk/src/lib/components/descope/descope.component.spec.ts @@ -55,7 +55,12 @@ describe('DescopeComponent', () => { component.locale = 'en-US'; component.success = new EventEmitter(); component.error = new EventEmitter(); - component.logger = { info: jest.fn(), error: jest.fn(), warn: jest.fn() }; + component.logger = { + info: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + debug: jest.fn() + }; component.errorTransformer = jest.fn(); component.client = {}; component.form = {}; diff --git a/projects/angular-sdk/src/lib/components/role-management/role-management.component.spec.ts b/projects/angular-sdk/src/lib/components/role-management/role-management.component.spec.ts index 38847d3..9336d66 100644 --- a/projects/angular-sdk/src/lib/components/role-management/role-management.component.spec.ts +++ b/projects/angular-sdk/src/lib/components/role-management/role-management.component.spec.ts @@ -51,7 +51,12 @@ describe('DescopeRoleManagementComponent', () => { component.projectId = '123'; component.tenant = 'tenant-1'; component.widgetId = 'widget-1'; - component.logger = { info: jest.fn(), error: jest.fn(), warn: jest.fn() }; + component.logger = { + info: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + debug: jest.fn() + }; fixture.detectChanges(); }); diff --git a/projects/angular-sdk/src/lib/components/user-management/user-management.component.spec.ts b/projects/angular-sdk/src/lib/components/user-management/user-management.component.spec.ts index 1d7cc67..ea3b570 100644 --- a/projects/angular-sdk/src/lib/components/user-management/user-management.component.spec.ts +++ b/projects/angular-sdk/src/lib/components/user-management/user-management.component.spec.ts @@ -51,7 +51,12 @@ describe('DescopeUserManagementComponent', () => { component.projectId = '123'; component.tenant = 'tenant-1'; component.widgetId = 'widget-1'; - component.logger = { info: jest.fn(), error: jest.fn(), warn: jest.fn() }; + component.logger = { + info: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + debug: jest.fn() + }; fixture.detectChanges(); }); diff --git a/projects/angular-sdk/src/lib/components/user-profile/user-profile.component.spec.ts b/projects/angular-sdk/src/lib/components/user-profile/user-profile.component.spec.ts new file mode 100644 index 0000000..da8c4c6 --- /dev/null +++ b/projects/angular-sdk/src/lib/components/user-profile/user-profile.component.spec.ts @@ -0,0 +1,93 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { UserProfileComponent } from './user-profile.component'; +import createSdk from '@descope/web-js-sdk'; +import { DescopeAuthConfig } from '../../types/types'; +import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core'; +import mocked = jest.mocked; + +jest.mock('@descope/web-js-sdk'); +//Mock DescopeUserProfileWidget +jest.mock('@descope/user-profile-widget', () => { + return jest.fn(() => { + // Create a mock DOM element + return document.createElement('descope-user-profile-widget'); + }); +}); + +describe('DescopeUserProfileComponent', () => { + let component: UserProfileComponent; + let fixture: ComponentFixture; + let mockedCreateSdk: jest.Mock; + const onSessionTokenChangeSpy = jest.fn(); + const onAuditChangeSpy = jest.fn(); + const afterRequestHooksSpy = jest.fn(); + const mockConfig: DescopeAuthConfig = { + projectId: 'someProject' + }; + + beforeEach(() => { + mockedCreateSdk = mocked(createSdk); + + mockedCreateSdk.mockReturnValue({ + onSessionTokenChange: onSessionTokenChangeSpy, + onAuditChange: onAuditChangeSpy, + httpClient: { + hooks: { + afterRequest: afterRequestHooksSpy + } + } + }); + + TestBed.configureTestingModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + providers: [ + DescopeAuthConfig, + { provide: DescopeAuthConfig, useValue: mockConfig } + ] + }); + + fixture = TestBed.createComponent(UserProfileComponent); + component = fixture.componentInstance; + component.projectId = '123'; + component.widgetId = 'widget-1'; + component.logout = new EventEmitter(); + component.logger = { + info: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + debug: jest.fn() + }; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + const html: HTMLElement = fixture.nativeElement; + const webComponentHtml = html.querySelector('descope-user-profile-widget'); + expect(webComponentHtml).toBeDefined(); + }); + + it('should correctly setup attributes based on inputs', () => { + const html: HTMLElement = fixture.nativeElement; + const webComponentHtml = html.querySelector('descope-user-profile-widget')!; + expect(webComponentHtml.getAttribute('project-id')).toStrictEqual('123'); + expect(webComponentHtml.getAttribute('widget-id')).toStrictEqual( + 'widget-1' + ); + expect(webComponentHtml.getAttribute('logger')).toBeDefined(); + }); + + it('should emit logout when web component emits logout', () => { + const html: HTMLElement = fixture.nativeElement; + const webComponentHtml = html.querySelector('descope-user-profile-widget')!; + + const event = { + detail: 'logout' + }; + component.logout.subscribe((e) => { + expect(afterRequestHooksSpy).toHaveBeenCalled(); + expect(e.detail).toHaveBeenCalledWith(event.detail); + }); + webComponentHtml.dispatchEvent(new CustomEvent('logout', event)); + }); +}); diff --git a/projects/angular-sdk/src/lib/components/user-profile/user-profile.component.ts b/projects/angular-sdk/src/lib/components/user-profile/user-profile.component.ts new file mode 100644 index 0000000..823d8a2 --- /dev/null +++ b/projects/angular-sdk/src/lib/components/user-profile/user-profile.component.ts @@ -0,0 +1,72 @@ +import { + Component, + ElementRef, + EventEmitter, + Input, + OnChanges, + OnInit, + Output +} from '@angular/core'; +import DescopeUserProfileWidget from '@descope/user-profile-widget'; +import { ILogger } from '@descope/web-component'; +import { DescopeAuthConfig } from '../../types/types'; + +@Component({ + selector: 'user-profile', + standalone: true, + template: '' +}) +export class UserProfileComponent implements OnInit, OnChanges { + projectId: string; + baseUrl?: string; + @Input() widgetId: string; + + @Input() theme: 'light' | 'dark' | 'os'; + @Input() debug: boolean; + @Input() logger: ILogger; + + @Output() logout: EventEmitter = new EventEmitter(); + + private readonly webComponent = new DescopeUserProfileWidget(); + + constructor( + private elementRef: ElementRef, + descopeConfig: DescopeAuthConfig + ) { + this.projectId = descopeConfig.projectId; + this.baseUrl = descopeConfig.baseUrl; + } + + ngOnInit() { + this.setupWebComponent(); + this.elementRef.nativeElement.appendChild(this.webComponent); + } + + ngOnChanges(): void { + this.setupWebComponent(); + } + + private setupWebComponent() { + this.webComponent.setAttribute('project-id', this.projectId); + this.webComponent.setAttribute('widget-id', this.widgetId); + if (this.baseUrl) { + this.webComponent.setAttribute('base-url', this.baseUrl); + } + if (this.theme) { + this.webComponent.setAttribute('theme', this.theme); + } + if (this.debug) { + this.webComponent.setAttribute('debug', this.debug.toString()); + } + + if (this.logger) { + (this.webComponent as any).logger = this.logger; + } + + if (this.logout) { + this.webComponent.addEventListener('logout', (e: Event) => { + this.logout?.emit(e as CustomEvent); + }); + } + } +} diff --git a/projects/angular-sdk/src/lib/descope-auth.module.ts b/projects/angular-sdk/src/lib/descope-auth.module.ts index 5aeea1b..cafec73 100644 --- a/projects/angular-sdk/src/lib/descope-auth.module.ts +++ b/projects/angular-sdk/src/lib/descope-auth.module.ts @@ -13,6 +13,7 @@ import { UserManagementComponent } from './components/user-management/user-manag import { RoleManagementComponent } from './components/role-management/role-management.component'; import { AccessKeyManagementComponent } from './components/access-key-management/access-key-management.component'; import { AuditManagementComponent } from './components/audit-management/audit-management.component'; +import { UserProfileComponent } from './components/user-profile/user-profile.component'; import { DescopeAuthConfig } from './types/types'; @NgModule({ @@ -25,7 +26,8 @@ import { DescopeAuthConfig } from './types/types'; UserManagementComponent, RoleManagementComponent, AccessKeyManagementComponent, - AuditManagementComponent + AuditManagementComponent, + UserProfileComponent ], exports: [ DescopeComponent, @@ -35,7 +37,8 @@ import { DescopeAuthConfig } from './types/types'; UserManagementComponent, RoleManagementComponent, AccessKeyManagementComponent, - AuditManagementComponent + AuditManagementComponent, + UserProfileComponent ] }) export class DescopeAuthModule { diff --git a/projects/angular-sdk/src/public-api.ts b/projects/angular-sdk/src/public-api.ts index 5bf3477..f4cb791 100644 --- a/projects/angular-sdk/src/public-api.ts +++ b/projects/angular-sdk/src/public-api.ts @@ -14,4 +14,5 @@ export * from './lib/components/user-management/user-management.component'; export * from './lib/components/role-management/role-management.component'; export * from './lib/components/access-key-management/access-key-management.component'; export * from './lib/components/audit-management/audit-management.component'; +export * from './lib/components/user-profile/user-profile.component'; export * from './lib/types/types'; diff --git a/projects/demo-app/src/app/app-routing.module.ts b/projects/demo-app/src/app/app-routing.module.ts index a0de657..b01224e 100644 --- a/projects/demo-app/src/app/app-routing.module.ts +++ b/projects/demo-app/src/app/app-routing.module.ts @@ -8,6 +8,7 @@ import { ManageUsersComponent } from './manage-users/manage-users.component'; import { ManageRolesComponent } from './manage-roles/manage-roles.component'; import { ManageAccessKeysComponent } from './manage-access-keys/manage-access-keys.component'; import { ManageAuditComponent } from './manage-audit/manage-audit.component'; +import { MyUserProfileComponent } from './my-user-profile/my-user-profile.component'; const routes: Routes = [ { @@ -21,6 +22,7 @@ const routes: Routes = [ { path: 'manage-roles', component: ManageRolesComponent }, { path: 'manage-access-keys', component: ManageAccessKeysComponent }, { path: 'manage-audit', component: ManageAuditComponent }, + { path: 'my-user-profile', component: MyUserProfileComponent }, { path: '**', component: HomeComponent } ]; diff --git a/projects/demo-app/src/app/app.module.ts b/projects/demo-app/src/app/app.module.ts index 7220987..38ba307 100644 --- a/projects/demo-app/src/app/app.module.ts +++ b/projects/demo-app/src/app/app.module.ts @@ -14,6 +14,7 @@ import { ManageUsersComponent } from './manage-users/manage-users.component'; import { ManageRolesComponent } from './manage-roles/manage-roles.component'; import { ManageAccessKeysComponent } from './manage-access-keys/manage-access-keys.component'; import { ManageAuditComponent } from './manage-audit/manage-audit.component'; +import { MyUserProfileComponent } from './my-user-profile/my-user-profile.component'; import { HttpClientModule, provideHttpClient, @@ -34,7 +35,8 @@ export function initializeApp(authService: DescopeAuthService) { ManageUsersComponent, ManageRolesComponent, ManageAccessKeysComponent, - ManageAuditComponent + ManageAuditComponent, + MyUserProfileComponent ], imports: [ BrowserModule, diff --git a/projects/demo-app/src/app/home/home.component.html b/projects/demo-app/src/app/home/home.component.html index 56c4548..6406398 100644 --- a/projects/demo-app/src/app/home/home.component.html +++ b/projects/demo-app/src/app/home/home.component.html @@ -25,5 +25,8 @@

Hello {{ userName }}

Manage Access Keys + diff --git a/projects/demo-app/src/app/home/home.component.ts b/projects/demo-app/src/app/home/home.component.ts index 1814426..5ba02ba 100644 --- a/projects/demo-app/src/app/home/home.component.ts +++ b/projects/demo-app/src/app/home/home.component.ts @@ -76,4 +76,10 @@ export class HomeComponent implements OnInit { manageAudit() { this.router.navigate(['/manage-audit']).catch((err) => console.error(err)); } + + myUserProfile() { + this.router + .navigate(['/my-user-profile']) + .catch((err) => console.error(err)); + } } diff --git a/projects/demo-app/src/app/my-user-profile/my-user-profile.component.html b/projects/demo-app/src/app/my-user-profile/my-user-profile.component.html new file mode 100644 index 0000000..de8b508 --- /dev/null +++ b/projects/demo-app/src/app/my-user-profile/my-user-profile.component.html @@ -0,0 +1,6 @@ + diff --git a/projects/demo-app/src/app/my-user-profile/my-user-profile.component.spec.ts b/projects/demo-app/src/app/my-user-profile/my-user-profile.component.spec.ts new file mode 100644 index 0000000..7cb54db --- /dev/null +++ b/projects/demo-app/src/app/my-user-profile/my-user-profile.component.spec.ts @@ -0,0 +1,36 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MyUserProfileComponent } from './my-user-profile.component'; +import createSdk from '@descope/web-js-sdk'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { DescopeAuthConfig } from '../../../../angular-sdk/src/lib/types/types'; +import mocked = jest.mocked; + +jest.mock('@descope/web-js-sdk'); + +describe('MyUserProfileComponent', () => { + let component: MyUserProfileComponent; + let fixture: ComponentFixture; + + let mockedCreateSdk: jest.Mock; + + beforeEach(() => { + mockedCreateSdk = mocked(createSdk); + mockedCreateSdk.mockReturnValue({}); + + TestBed.configureTestingModule({ + schemas: [NO_ERRORS_SCHEMA], + declarations: [MyUserProfileComponent], + providers: [ + DescopeAuthConfig, + { provide: DescopeAuthConfig, useValue: { projectId: 'test' } } + ] + }); + fixture = TestBed.createComponent(MyUserProfileComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/demo-app/src/app/my-user-profile/my-user-profile.component.ts b/projects/demo-app/src/app/my-user-profile/my-user-profile.component.ts new file mode 100644 index 0000000..0255f0d --- /dev/null +++ b/projects/demo-app/src/app/my-user-profile/my-user-profile.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; +import { environment } from '../../environments/environment'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-my-user-profile', + templateUrl: './my-user-profile.component.html' +}) +export class MyUserProfileComponent { + theme = (environment.descopeTheme as 'light' | 'dark' | 'os') ?? 'os'; + debugMode = environment.descopeDebugMode ?? false; + + onLogout(e: CustomEvent) { + console.log('SUCCESSFULLY LOGGED IN FROM WEB COMPONENT', e.detail); + this.router.navigate(['/login']).catch((err) => console.error(err)); + } + + constructor(private router: Router) {} +}