Skip to content

Commit

Permalink
fix: add Group entity to IDynamicPerson type and introduce typeguards…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
Mnickii authored and gavinbarron committed Sep 28, 2023
1 parent 118a2bb commit a7f0fe2
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 16 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"build:mgt-chat": "lerna run build --scope @microsoft/mgt-chat",
"build:react-contoso": "lerna run build --scope react-contoso",
"bundle": "cd ./packages/mgt && npm run bundle",
"clean": "lerna run --parallel --stream --scope @microsoft/* clean",
"clean": "lerna run --parallel --stream --scope '@microsoft/*' clean",
"lint:eslint": "eslint -c .eslintrc.js --quiet 'packages/*/src/**/*.ts'",
"lint:eslint:loud": "eslint -c .eslintrc.js 'packages/*/src/**/*.ts'",
"lint:styles": "stylelint './packages/mgt-components/**/*.{css,scss}'",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
import '../../styles/style-helper';
import '../sub-components/mgt-spinner/mgt-spinner';
import { debounce, isValidEmail } from '../../utils/Utils';
import { MgtPerson } from '../mgt-person/mgt-person';
import { MgtPerson, defaultPersonProperties } from '../mgt-person/mgt-person';
import { PersonCardInteraction } from '../PersonCardInteraction';
import { MgtFlyout } from '../sub-components/mgt-flyout/mgt-flyout';
import { styles } from './mgt-people-picker-css';
Expand Down Expand Up @@ -640,7 +640,7 @@ export class MgtPeoplePicker extends MgtTemplatedComponent {
for (const id in userIds) {
const userId = userIds[id];
try {
const personDetails = await getUser(graph, userId);
const personDetails = await getUser(graph, userId, defaultPersonProperties);
this.addPerson(personDetails);
} catch (e: unknown) {
// This caters for allow-any-email property if it's enabled on the component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { MgtOrganization } from '../mgt-organization/mgt-organization';
import { MgtProfile } from '../mgt-profile/mgt-profile';
import { MgtPersonCardConfig, MgtPersonCardState } from './mgt-person-card.types';
import { strings } from './strings';
import { isUser } from '../../graph/entityType';

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

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

this._cardState = historyState.state;
this._personDetails = historyState.state;
this._personDetails = historyState.personDetails;
this.personImage = historyState.personImage;
this.loadSections();
}
Expand Down Expand Up @@ -644,7 +645,7 @@ export class MgtPersonCard extends MgtTemplatedComponent {
*/
protected renderPersonSubtitle(person?: IDynamicPerson): TemplateResult {
person = person || this.internalPersonDetails;
if (!person.department) {
if (!isUser(person) || !person.department) {
return;
}
return html`
Expand Down Expand Up @@ -1016,8 +1017,11 @@ export class MgtPersonCard extends MgtTemplatedComponent {

// check if personDetail already populated
if (this.personDetails) {
const user = this.personDetails as User;
const id = user.userPrincipalName || user.id;
const user = this.personDetails;
let id: string;
if (isUser(user)) {
id = user.userPrincipalName || user.id;
}

// if we have an id but no email, we should get data from the graph
// in some graph calls, the user object does not contain the email
Expand Down
16 changes: 8 additions & 8 deletions packages/mgt-components/src/components/mgt-person/mgt-person.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
customElementHelper,
mgtHtml
} from '@microsoft/mgt-element';
import { Contact, Presence } from '@microsoft/microsoft-graph-types';
import { Presence } from '@microsoft/microsoft-graph-types';
import { html, TemplateResult, nothing } from 'lit';
import { property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
Expand All @@ -32,14 +32,15 @@ import { PersonCardInteraction } from './../PersonCardInteraction';
import { styles } from './mgt-person-css';
import { MgtPersonConfig, PersonViewType, avatarType } from './mgt-person-types';
import { strings } from './strings';
import { isUser, isContact } from '../../graph/entityType';
import { ifDefined } from 'lit/directives/if-defined.js';

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

/**
* Person properties part of original set provided by graph by default
*/
const defaultPersonProperties = [
export const defaultPersonProperties = [
'businessPhones',
'displayName',
'givenName',
Expand All @@ -51,7 +52,8 @@ const defaultPersonProperties = [
'preferredLanguage',
'surname',
'userPrincipalName',
'id'
'id',
'userType'
];

/**
Expand Down Expand Up @@ -1205,15 +1207,13 @@ export class MgtPerson extends MgtTemplatedComponent {
person = this.personDetailsInternal;
}

if ((person as Contact).initials) {
return (person as Contact).initials;
if (isContact(person)) {
return person.initials;
}

let initials = '';
if (person.givenName) {
if (isUser(person)) {
initials += person.givenName[0].toUpperCase();
}
if (person.surname) {
initials += person.surname[0].toUpperCase();
}

Expand Down
20 changes: 20 additions & 0 deletions packages/mgt-components/src/graph/entityType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* -------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License.
* See License in the project root for license information.
* -------------------------------------------------------------------------------------------
*/

import { IDynamicPerson, IContact, IGroup, IUser } from './types';

export const isGroup = (obj: IDynamicPerson): obj is IGroup => {
return 'groupTypes' in obj;
};

export const isUser = (obj: IDynamicPerson): obj is IUser => {
return 'personType' in obj || 'userType' in obj;
};

export const isContact = (obj: IDynamicPerson): obj is IContact => {
return 'initials' in obj;
};
6 changes: 5 additions & 1 deletion packages/mgt-components/src/graph/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
* In addition, this custom type also defines the optional `personImage` property,
* which is used to pass the image around to other components as part of the person object.
*/
export type IDynamicPerson = (MicrosoftGraph.User | MicrosoftGraph.Person | MicrosoftGraph.Contact) & {
export type IUser = MicrosoftGraph.User | MicrosoftGraph.Person;
export type IContact = MicrosoftGraph.Contact;
export type IGroup = MicrosoftGraph.Group;

export type IDynamicPerson = (IUser | IContact | IGroup) & {
/**
* personDetails.personImage is a toolkit injected property to pass image between components
* an optimization to avoid fetching the image when unnecessary.
Expand Down
1 change: 1 addition & 0 deletions packages/mgt-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from './graph/graph.photos';
export * from './graph/cacheStores';
export * from './styles/theme-manager';
export * from './utils/Utils';
export * from './graph/entityType';
29 changes: 29 additions & 0 deletions stories/components/peoplePicker/peoplePicker.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,35 @@ export const selectionChangedEvent = () => html`
</script>
`;

export const getEntityType = () => html`
<mgt-people-picker
type="person">
</mgt-people-picker>
<!-- Group entityType -->
<!-- <mgt-people-picker type="group"></mgt-people-picker> -->
<div class="entity-type"></div>
<script type="module">
import { isUser, isContact, isGroup } from '@microsoft/mgt-components';
let entityType;
const output = document.querySelector('.entity-type');
const handleSelection = (e) => {
const selected = e.detail[0];
if (isGroup(selected)) {
entityType = 'group'
} else if (isUser(selected)) {
entityType = 'user'
} else if (isContact(selected)) {
entityType = 'contact'
}
output.innerHTML = '<b>entityType:</b>' + entityType;
}
document.querySelector('mgt-people-picker').addEventListener('selectionChanged', e => handleSelection(e));
</script>
`;

export const selectGroupsById = () => html`
<mgt-people-picker></mgt-people-picker>
<!-- Check the js tab for example -->
Expand Down

0 comments on commit a7f0fe2

Please sign in to comment.