Skip to content

Commit 711a233

Browse files
authored
feat: optionally pass the group-id value from tasks to people picker (#2200)
* Add story for mgt-task group-id attribute * When there are no batch requests, return dict from cache * Pass group-id to people-picker if defined * Update note to show missing permissions from sandbox tenant * Execute batch requests if they exist * Add containerID to track group the plan belongs to * Get the group linked to a task from the plan * Refactor handling date change to function * groupId is used to fetch tasks for the ID This means when is supplied, the loaded tasks are specific to that group. * Change variable name to be more clear * Fix withCodeEditor import path in tasks stories
1 parent c60567d commit 711a233

File tree

4 files changed

+93
-52
lines changed

4 files changed

+93
-52
lines changed

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

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
import { Person, PlannerAssignments, PlannerTask, User } from '@microsoft/microsoft-graph-types';
99
import { Contact, OutlookTask, OutlookTaskFolder } from '@microsoft/microsoft-graph-types-beta';
10-
import { customElement, html, property } from 'lit-element';
10+
import { customElement, html, property, state, TemplateResult } from 'lit-element';
11+
import { ifDefined } from 'lit-html/directives/if-defined';
1112
import { classMap } from 'lit-html/directives/class-map';
1213
import { repeat } from 'lit-html/directives/repeat';
1314
import { ComponentMediaQuery, Providers, ProviderState, MgtTemplatedComponent } from '@microsoft/mgt-element';
@@ -230,6 +231,7 @@ export class MgtTasks extends MgtTemplatedComponent {
230231
this._newTaskDueDate = null;
231232
this._newTaskName = '';
232233
this._newTaskGroupId = '';
234+
this._newTaskContainerId = '';
233235
}
234236
}
235237

@@ -345,6 +347,7 @@ export class MgtTasks extends MgtTemplatedComponent {
345347
@property() private _newTaskDueDate: Date;
346348
@property() private _newTaskGroupId: string;
347349
@property() private _newTaskFolderId: string;
350+
@property() private _newTaskContainerId: string;
348351
@property() private _groups: ITaskGroup[];
349352
@property() private _folders: ITaskFolder[];
350353
@property() private _tasks: ITask[];
@@ -357,7 +360,7 @@ export class MgtTasks extends MgtTemplatedComponent {
357360
@property() private _currentGroup: string;
358361
@property() private _currentFolder: string;
359362

360-
private _me: User = null;
363+
@state() private _me: User = null;
361364
private previousMediaQuery: ComponentMediaQuery;
362365

363366
constructor() {
@@ -461,18 +464,9 @@ export class MgtTasks extends MgtTemplatedComponent {
461464
* trigger the element to update.
462465
*/
463466
protected render() {
464-
let tasks = this._tasks
465-
.filter(task => this.isTaskInSelectedGroupFilter(task))
466-
.filter(task => this.isTaskInSelectedFolderFilter(task))
467-
.filter(task => !this._hiddenTasks.includes(task.id));
468-
469-
if (this.taskFilter) {
470-
tasks = tasks.filter(task => this.taskFilter(task._raw));
471-
}
472-
473467
const loadingTask = this._inTaskLoad && !this._hasDoneInitialLoad ? this.renderLoadingTask() : null;
474468

475-
let header;
469+
let header: TemplateResult;
476470

477471
if (!this.hideHeader) {
478472
header = html`
@@ -487,7 +481,7 @@ export class MgtTasks extends MgtTemplatedComponent {
487481
<div class="Tasks" dir=${this.direction}>
488482
${this._isNewTaskVisible ? this.renderNewTask() : null} ${loadingTask}
489483
${repeat(
490-
tasks,
484+
this._tasks,
491485
task => task.id,
492486
task => this.renderTask(task)
493487
)}
@@ -513,10 +507,9 @@ export class MgtTasks extends MgtTemplatedComponent {
513507
}
514508

515509
this._inTaskLoad = true;
516-
let meTask;
517510
if (!this._me) {
518511
const graph = provider.graph.forComponent(this);
519-
meTask = getMe(graph);
512+
this._me = await getMe(graph);
520513
}
521514

522515
if (this.groupId && this.dataSource === TasksSource.planner) {
@@ -531,8 +524,13 @@ export class MgtTasks extends MgtTemplatedComponent {
531524
await this._loadAllTasks(ts);
532525
}
533526

534-
if (meTask) {
535-
this._me = await meTask;
527+
this._tasks = this._tasks
528+
.filter(task => this.isTaskInSelectedGroupFilter(task))
529+
.filter(task => this.isTaskInSelectedFolderFilter(task))
530+
.filter(task => !this._hiddenTasks.includes(task.id));
531+
532+
if (this.taskFilter) {
533+
this._tasks = this._tasks.filter(task => this.taskFilter(task._raw));
536534
}
537535

538536
this._inTaskLoad = false;
@@ -792,6 +790,27 @@ export class MgtTasks extends MgtTemplatedComponent {
792790
}
793791
}
794792

793+
private addNewTaskButtonClick(e: MouseEvent) {
794+
this.isNewTaskVisible = !this.isNewTaskVisible;
795+
}
796+
797+
private handleNewTaskDateChange(e: Event) {
798+
const value = (e.target as HTMLInputElement).value;
799+
if (value) {
800+
this._newTaskDueDate = new Date(value + 'T17:00');
801+
} else {
802+
this._newTaskDueDate = null;
803+
}
804+
}
805+
806+
private handleSelectedPlan(e: Event) {
807+
this._newTaskGroupId = (e.target as HTMLInputElement).value;
808+
if (this.dataSource === TasksSource.planner) {
809+
const task = this._groups.filter(iTask => iTask.id === this._newTaskGroupId);
810+
this._newTaskContainerId = task.pop()?.containerId ?? this._newTaskContainerId;
811+
}
812+
}
813+
795814
private newTaskVisible(e: KeyboardEvent) {
796815
if (e.code === 'Enter') {
797816
this.isNewTaskVisible = false;
@@ -818,9 +837,7 @@ export class MgtTasks extends MgtTemplatedComponent {
818837
<div
819838
tabindex="0"
820839
class="AddBarItem NewTaskButton"
821-
@click="${() => {
822-
this.isNewTaskVisible = !this.isNewTaskVisible;
823-
}}"
840+
@click="${this.addNewTaskButtonClick}"
824841
@keydown="${this.newTaskButtonKeydown}"
825842
>
826843
<span class="TaskIcon"></span>
@@ -938,6 +955,9 @@ export class MgtTasks extends MgtTemplatedComponent {
938955
const groups = this._groups;
939956
if (groups.length > 0 && !this._newTaskGroupId) {
940957
this._newTaskGroupId = groups[0].id;
958+
if (this.dataSource === TasksSource.planner) {
959+
this._newTaskContainerId = groups[0].containerId;
960+
}
941961
}
942962
const group =
943963
this.dataSource === TasksSource.todo
@@ -954,9 +974,7 @@ export class MgtTasks extends MgtTemplatedComponent {
954974
${this.renderPlannerIcon()}
955975
<select aria-label="new task group"
956976
.value="${this._newTaskGroupId}"
957-
@change="${(e: Event) => {
958-
this._newTaskGroupId = (e.target as HTMLInputElement).value;
959-
}}"
977+
@change="${this.handleSelectedPlan}"
960978
>
961979
${this._groups.map(
962980
plan => html`
@@ -1009,14 +1027,7 @@ export class MgtTasks extends MgtTemplatedComponent {
10091027
aria-label="new-taskDate-input"
10101028
role="textbox"
10111029
.value="${this.dateToInputValue(this._newTaskDueDate)}"
1012-
@change="${(e: Event) => {
1013-
const value = (e.target as HTMLInputElement).value;
1014-
if (value) {
1015-
this._newTaskDueDate = new Date(value + 'T17:00');
1016-
} else {
1017-
this._newTaskDueDate = null;
1018-
}
1019-
}}"
1030+
@change="${this.handleNewTaskDateChange}"
10201031
/>
10211032
</span>
10221033
`;
@@ -1260,6 +1271,7 @@ export class MgtTasks extends MgtTemplatedComponent {
12601271

12611272
private renderAssignedPeople(task: ITask) {
12621273
let assignedPeopleHTML = null;
1274+
let assignedGroupId: string;
12631275

12641276
const taskAssigneeClasses = {
12651277
NewTaskAssignee: task === null,
@@ -1282,6 +1294,16 @@ export class MgtTasks extends MgtTemplatedComponent {
12821294
const taskId = task ? task.id : 'newTask';
12831295
taskAssigneeClasses[`flyout-${taskId}`] = true;
12841296

1297+
if (!this.newTaskVisible) {
1298+
const planId = task?._raw?.planId;
1299+
if (planId) {
1300+
const group = this._groups.filter(group => group.id === planId);
1301+
assignedGroupId = group.pop()?.containerId;
1302+
}
1303+
}
1304+
1305+
const planGroupId = this.isNewTaskVisible ? this._newTaskContainerId : assignedGroupId;
1306+
12851307
assignedPeopleHTML = html`
12861308
<mgt-people
12871309
class="people-${taskId}"
@@ -1307,6 +1329,7 @@ export class MgtTasks extends MgtTemplatedComponent {
13071329
<div slot="flyout" class=${classMap({ Picker: true })}>
13081330
<mgt-people-picker
13091331
class="picker-${taskId}"
1332+
.groupId=${ifDefined(planGroupId)}
13101333
@click=${(e: MouseEvent) => e.stopPropagation()}
13111334
@keydown=${(e: KeyboardEvent) => {
13121335
if (e.code === 'Enter') {

packages/mgt-components/src/components/mgt-tasks/task-sources.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ export interface ITaskGroup {
174174
* @memberof ITaskGroup
175175
*/
176176
_raw?: any;
177+
178+
/**
179+
* Plan Container ID. Same as the group ID of the group in the plan.
180+
*
181+
* @type {string}
182+
* @memberof ITaskGroup
183+
*/
184+
containerId?: string;
177185
}
178186
/**
179187
* A common interface for both planner and todo tasks
@@ -324,8 +332,9 @@ export class PlannerTaskSource extends TaskSourceBase implements ITaskSource {
324332
*/
325333
public async getTaskGroups(): Promise<ITaskGroup[]> {
326334
const plans = await getAllMyPlannerPlans(this.graph);
327-
328-
return plans.map(plan => ({ id: plan.id, title: plan.title } as ITaskGroup));
335+
return plans.map(
336+
plan => ({ id: plan.id, title: plan.title, containerId: plan?.container?.containerId } as ITaskGroup)
337+
);
329338
}
330339

331340
/**

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

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { User } from '@microsoft/microsoft-graph-types';
1010

1111
import { findPeople, PersonType } from './graph.people';
1212
import { schemas } from './cacheStores';
13-
import { UserType } from '..';
1413
import { GraphRequest } from '@microsoft/microsoft-graph-client';
1514

1615
/**
@@ -221,29 +220,31 @@ export async function getUsersForUserIds(
221220
}
222221
}
223222
try {
224-
const responses = await batch.executeAll();
225-
// iterate over userIds to ensure the order of ids
226-
for (const id of userIds) {
227-
const response = responses.get(id);
228-
if (response && response.content) {
229-
const user = response.content;
230-
if (searchInput) {
231-
const displayName = user?.displayName.toLowerCase();
232-
if (displayName.contains(searchInput)) {
233-
peopleSearchMatches[id] = user;
223+
if (batch.hasRequests) {
224+
const responses = await batch.executeAll();
225+
// iterate over userIds to ensure the order of ids
226+
for (const id of userIds) {
227+
const response = responses.get(id);
228+
if (response && response.content) {
229+
const user = response.content;
230+
if (searchInput) {
231+
const displayName = user?.displayName.toLowerCase();
232+
if (displayName.contains(searchInput)) {
233+
peopleSearchMatches[id] = user;
234+
}
235+
} else {
236+
peopleDict[id] = user;
234237
}
235-
} else {
236-
peopleDict[id] = user;
237-
}
238238

239-
if (getIsUsersCacheEnabled()) {
240-
cache.putValue(id, { user: JSON.stringify(user) });
239+
if (getIsUsersCacheEnabled()) {
240+
cache.putValue(id, { user: JSON.stringify(user) });
241+
}
242+
}
243+
if (searchInput && Object.keys(peopleSearchMatches).length) {
244+
return Promise.all(Object.values(peopleSearchMatches));
241245
}
242246
}
243247
}
244-
if (searchInput && Object.keys(peopleSearchMatches).length) {
245-
return Promise.all(Object.values(peopleSearchMatches));
246-
}
247248
return Promise.all(Object.values(peopleDict));
248249
} catch (_) {
249250
// fallback to making the request one by one

stories/components/tasks.stories.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ export const tasks = () => html`
2222
<mgt-tasks></mgt-tasks>
2323
`;
2424

25+
export const tasksWithGroupId = () => html`
26+
<mgt-tasks group-id="45327068-6785-4073-8553-a750d6c16a45"></mgt-tasks>
27+
<!--
28+
NOTE: the default sandbox tenant doesn't have the required Tasks.ReadWrite and
29+
Group.ReadWrite.All permissions. Test this component in your tenant.
30+
-->
31+
`;
32+
2533
export const darkTheme = () => html`
2634
<mgt-tasks class="mgt-dark"></mgt-tasks>
2735
<style>

0 commit comments

Comments
 (0)