Skip to content

Commit b3bc50d

Browse files
authored
fix: add Group entity to IDynamicPerson type and introduce typeguards to find the entity type (#2688)
add Group type to IDynamicPerson implement type guards refactor getInitials to use type guard functions add entityType story export typeguards
1 parent 247f37a commit b3bc50d

File tree

8 files changed

+74
-16
lines changed

8 files changed

+74
-16
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"build:mgt-spfx": "lerna run build --scope @microsoft/mgt-spfx",
2323
"build:react-contoso": "lerna run build --scope react-contoso",
2424
"bundle": "cd ./packages/mgt && npm run bundle",
25-
"clean": "lerna run --parallel --stream --scope @microsoft/* clean",
25+
"clean": "lerna run --parallel --stream --scope '@microsoft/*' clean",
2626
"lint:eslint": "eslint -c .eslintrc.js --quiet 'packages/*/src/**/*.ts'",
2727
"lint:eslint:loud": "eslint -c .eslintrc.js 'packages/*/src/**/*.ts'",
2828
"lint:styles": "stylelint './packages/mgt-components/**/*.{css,scss}'",

packages/mgt-components/src/components/mgt-people-picker/mgt-people-picker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {
3737
import '../../styles/style-helper';
3838
import '../sub-components/mgt-spinner/mgt-spinner';
3939
import { debounce, isValidEmail } from '../../utils/Utils';
40-
import { MgtPerson } from '../mgt-person/mgt-person';
40+
import { MgtPerson, defaultPersonProperties } from '../mgt-person/mgt-person';
4141
import { PersonCardInteraction } from '../PersonCardInteraction';
4242
import { MgtFlyout } from '../sub-components/mgt-flyout/mgt-flyout';
4343
import { styles } from './mgt-people-picker-css';
@@ -640,7 +640,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
640640
for (const id in userIds) {
641641
const userId = userIds[id];
642642
try {
643-
const personDetails = await getUser(graph, userId);
643+
const personDetails = await getUser(graph, userId, defaultPersonProperties);
644644
this.addPerson(personDetails);
645645
} catch (e: unknown) {
646646
// This caters for allow-any-email property if it's enabled on the component

packages/mgt-components/src/components/mgt-person-card/mgt-person-card.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { MgtOrganization } from '../mgt-organization/mgt-organization';
3636
import { MgtProfile } from '../mgt-profile/mgt-profile';
3737
import { MgtPersonCardConfig, MgtPersonCardState } from './mgt-person-card.types';
3838
import { strings } from './strings';
39+
import { isUser } from '../../graph/entityType';
3940

4041
import '../sub-components/mgt-spinner/mgt-spinner';
4142

@@ -476,7 +477,7 @@ export class MgtPersonCard extends MgtTemplatedComponent {
476477
this._history = [];
477478

478479
this._cardState = historyState.state;
479-
this._personDetails = historyState.state;
480+
this._personDetails = historyState.personDetails;
480481
this.personImage = historyState.personImage;
481482
this.loadSections();
482483
}
@@ -644,7 +645,7 @@ export class MgtPersonCard extends MgtTemplatedComponent {
644645
*/
645646
protected renderPersonSubtitle(person?: IDynamicPerson): TemplateResult {
646647
person = person || this.internalPersonDetails;
647-
if (!person.department) {
648+
if (!isUser(person) || !person.department) {
648649
return;
649650
}
650651
return html`
@@ -1016,8 +1017,11 @@ export class MgtPersonCard extends MgtTemplatedComponent {
10161017

10171018
// check if personDetail already populated
10181019
if (this.personDetails) {
1019-
const user = this.personDetails as User;
1020-
const id = user.userPrincipalName || user.id;
1020+
const user = this.personDetails;
1021+
let id: string;
1022+
if (isUser(user)) {
1023+
id = user.userPrincipalName || user.id;
1024+
}
10211025

10221026
// if we have an id but no email, we should get data from the graph
10231027
// in some graph calls, the user object does not contain the email

packages/mgt-components/src/components/mgt-person/mgt-person.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
customElementHelper,
1414
mgtHtml
1515
} from '@microsoft/mgt-element';
16-
import { Contact, Presence } from '@microsoft/microsoft-graph-types';
16+
import { Presence } from '@microsoft/microsoft-graph-types';
1717
import { html, TemplateResult, nothing } from 'lit';
1818
import { property, state } from 'lit/decorators.js';
1919
import { classMap } from 'lit/directives/class-map.js';
@@ -32,14 +32,15 @@ import { PersonCardInteraction } from './../PersonCardInteraction';
3232
import { styles } from './mgt-person-css';
3333
import { MgtPersonConfig, PersonViewType, avatarType } from './mgt-person-types';
3434
import { strings } from './strings';
35+
import { isUser, isContact } from '../../graph/entityType';
3536
import { ifDefined } from 'lit/directives/if-defined.js';
3637

3738
export { PersonCardInteraction } from '../PersonCardInteraction';
3839

3940
/**
4041
* Person properties part of original set provided by graph by default
4142
*/
42-
const defaultPersonProperties = [
43+
export const defaultPersonProperties = [
4344
'businessPhones',
4445
'displayName',
4546
'givenName',
@@ -51,7 +52,8 @@ const defaultPersonProperties = [
5152
'preferredLanguage',
5253
'surname',
5354
'userPrincipalName',
54-
'id'
55+
'id',
56+
'userType'
5557
];
5658

5759
/**
@@ -1205,15 +1207,13 @@ export class MgtPerson extends MgtTemplatedComponent {
12051207
person = this.personDetailsInternal;
12061208
}
12071209

1208-
if ((person as Contact).initials) {
1209-
return (person as Contact).initials;
1210+
if (isContact(person)) {
1211+
return person.initials;
12101212
}
12111213

12121214
let initials = '';
1213-
if (person.givenName) {
1215+
if (isUser(person)) {
12141216
initials += person.givenName[0].toUpperCase();
1215-
}
1216-
if (person.surname) {
12171217
initials += person.surname[0].toUpperCase();
12181218
}
12191219

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* -------------------------------------------------------------------------------------------
3+
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
4+
* See License in the project root for license information.
5+
* -------------------------------------------------------------------------------------------
6+
*/
7+
8+
import { IDynamicPerson, IContact, IGroup, IUser } from './types';
9+
10+
export const isGroup = (obj: IDynamicPerson): obj is IGroup => {
11+
return 'groupTypes' in obj;
12+
};
13+
14+
export const isUser = (obj: IDynamicPerson): obj is IUser => {
15+
return 'personType' in obj || 'userType' in obj;
16+
};
17+
18+
export const isContact = (obj: IDynamicPerson): obj is IContact => {
19+
return 'initials' in obj;
20+
};

packages/mgt-components/src/graph/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
1414
* In addition, this custom type also defines the optional `personImage` property,
1515
* which is used to pass the image around to other components as part of the person object.
1616
*/
17-
export type IDynamicPerson = (MicrosoftGraph.User | MicrosoftGraph.Person | MicrosoftGraph.Contact) & {
17+
export type IUser = MicrosoftGraph.User | MicrosoftGraph.Person;
18+
export type IContact = MicrosoftGraph.Contact;
19+
export type IGroup = MicrosoftGraph.Group;
20+
21+
export type IDynamicPerson = (IUser | IContact | IGroup) & {
1822
/**
1923
* personDetails.personImage is a toolkit injected property to pass image between components
2024
* an optimization to avoid fetching the image when unnecessary.

packages/mgt-components/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export * from './components/components';
99
export * from './components/preview';
1010
export * from './graph/types';
1111
export * from './styles/theme-manager';
12+
export * from './graph/entityType';

stories/components/peoplePicker/peoplePicker.stories.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,35 @@ export const selectionChangedEvent = () => html`
4040
</script>
4141
`;
4242

43+
export const getEntityType = () => html`
44+
<mgt-people-picker
45+
type="person">
46+
</mgt-people-picker>
47+
<!-- Group entityType -->
48+
<!-- <mgt-people-picker type="group"></mgt-people-picker> -->
49+
50+
<div class="entity-type"></div>
51+
52+
<script type="module">
53+
import { isUser, isContact, isGroup } from '@microsoft/mgt-components';
54+
let entityType;
55+
const output = document.querySelector('.entity-type');
56+
const handleSelection = (e) => {
57+
const selected = e.detail[0];
58+
if (isGroup(selected)) {
59+
entityType = 'group'
60+
} else if (isUser(selected)) {
61+
entityType = 'user'
62+
} else if (isContact(selected)) {
63+
entityType = 'contact'
64+
}
65+
output.innerHTML = '<b>entityType:</b>' + entityType;
66+
}
67+
document.querySelector('mgt-people-picker').addEventListener('selectionChanged', e => handleSelection(e));
68+
69+
</script>
70+
`;
71+
4372
export const selectGroupsById = () => html`
4473
<mgt-people-picker></mgt-people-picker>
4574
<!-- Check the js tab for example -->

0 commit comments

Comments
 (0)