Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add Group entity to IDynamicPerson type and introduce typeguards to find the entity type #2688

Merged
merged 19 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"build:mgt-spfx": "lerna run build --scope @microsoft/mgt-spfx",
"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
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)) {
gavinbarron marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -9,3 +9,4 @@ export * from './components/components';
export * from './components/preview';
export * from './graph/types';
export * from './styles/theme-manager';
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
Loading