SharePoint 埋め込みコンテナーを作成して一覧表示するようにアプリを更新する

完了

この演習では、既存のプロジェクトを更新して、SharePoint Embedded Containers を作成して取得します。

フロントエンド React SPA にサインイン機能を追加する

まず、React SPA フロントエンド プロジェクトにユーザー サインインのサポートを追加します。 これには、Microsoft Authentication Library (MSAL) の構成コードを追加し、必要なサインインの詳細を使用して Microsoft Graph Toolkit を構成する必要があります。

認証プロバイダーを設定する

./src/index.tsx ファイルを見つけて開きます。

既存のインポートの後に、次のインポートを追加します。

import { Providers } from "@microsoft/mgt-element";
import { Msal2Provider } from "@microsoft/mgt-msal2-provider";
import * as Constants from "./common/constants"
import * as Scopes from "./common/scopes";

最後に、React レンダリング コードの前に次のコードを追加して、Microsoft Graph Toolkit のグローバル MSAL プロバイダーを構成します。

Providers.globalProvider = new Msal2Provider({
  clientId: Constants.CLIENT_ENTRA_APP_CLIENT_ID,
  authority: Constants.CLIENT_ENTRA_APP_AUTHORITY,
  scopes: [
    ...Scopes.GRAPH_OPENID_CONNECT_BASIC,
    Scopes.GRAPH_USER_READ_ALL,
    Scopes.GRAPH_FILES_READ_WRITE_ALL,
    Scopes.GRAPH_SITES_READ_ALL,
    Scopes.SPEMBEDDED_FILESTORAGECONTAINER_SELECTED
  ]
});

サインインとサインアウトプロセスを処理するようにアプリホームページを更新する

./src/App.tsx ファイルを見つけて開きます。 ファイルのインポートを logo.svg 削除し、残りのインポートを追加して更新して、次のコードを反映します。

import React, {
  useState, useEffect
} from "react";
import {
  Providers,
  ProviderState
} from "@microsoft/mgt-element";
import { Login } from "@microsoft/mgt-react";
import {
  FluentProvider,
  Text,
  webLightTheme
} from "@fluentui/react-components"
import './App.css';
import {
  InteractionRequiredAuthError,
  PublicClientApplication
} from "@azure/msal-browser";
import * as Scopes from "./common/scopes";
import * as Constants from "./common/constants";

import ステートメントの後に、次のカスタム React フックを追加して、現在のユーザーのサインイン状態を取得します。

function useIsSignedIn() {
  const [isSignedIn, setIsSignedIn] = useState(false);

  useEffect(() => {
    const updateState = async () => {
      const provider = Providers.globalProvider;
      setIsSignedIn(provider && provider.state === ProviderState.SignedIn);
    };

    Providers.onProviderUpdated(updateState);
    updateState();

    return () => {
      Providers.removeProviderUpdatedListener(updateState);
    }
  }, []);

  return isSignedIn;
}

次に、 宣言の App 後に次のコードを追加して を更新します。

const isSignedIn = useIsSignedIn();

const promptForContainerConsent = async (event: CustomEvent<undefined>): Promise<void> => {
  const containerScopes = {
    scopes: [Scopes.SPEMBEDDED_FILESTORAGECONTAINER_SELECTED],
    redirectUri: `${window.location.protocol}://${window.location.hostname}${(window.location.port === '80' || window.location.port === '443') ? '' : ':' + window.location.port}`
  };

  const msalInstance = new PublicClientApplication({
    auth: {
      clientId: Constants.CLIENT_ENTRA_APP_CLIENT_ID,
      authority: Constants.CLIENT_ENTRA_APP_AUTHORITY,
    },
    cache: {
      cacheLocation: 'localStorage',
      storeAuthStateInCookie: false,
    },
  });

  msalInstance.acquireTokenSilent(containerScopes)
    .then(response => {
      console.log('tokenResponse', JSON.stringify(response));
    })
    .catch(async (error) => {
      if (error instanceof InteractionRequiredAuthError) {
        return msalInstance.acquireTokenPopup(containerScopes);
      }
    });
}

このコードは、最初にユーザーの現在のサインイン状態を取得し、ユーザーがレンダリングのボタンを選択すると、アクセス トークンを構成して取得します。

最後に、コンポーネントのステートメントを次のように置き換えて、コンポーネントの return() レンダリングを更新します。

return (
  <FluentProvider theme={webLightTheme}>
    <div className="App">
      <Text size={900} weight='bold'>Sample SPA SharePoint Embedded App</Text>
      <Login loginCompleted={promptForContainerConsent} />
      <div>
      </div>
    </div>
  </FluentProvider>
);

React アプリの認証をテストする

次に、クライアント側の React アプリをテストして、認証が機能していることを確認します。

プロジェクトのルート フォルダーのコマンド ラインから、次のコマンドを実行します。

npm run start

このスクリプトは、クライアント側プロジェクト & サーバー側のプロジェクトをビルドし、起動し、クライアント側プロジェクトに対してブラウザーを起動します。

React アプリのスクリーンショット。

[ サインイン ] ボタンを選択し、Microsoft 365 テナントへの管理者アクセス権を持つ 職場と学校 のアカウントを使用してサインインします。

正常にサインインすると、サインインしたユーザーの名前と電子メールが表示された React アプリにリダイレクトされます。

サインインしているユーザーを含む React アプリのスクリーンショット。

コンソールで CtrlCキー + を押してサーバーを停止します。

コンテナーを一覧表示して選択する機能を追加する

基本的なプロジェクトのセットアップとユーザー認証をサポートするように構成した後、テナントのパーティションのコンテナーを一覧表示して選択するためのサポートを追加しましょう。

コンテナー管理は、サーバー側で取得する必要があるアクセス トークンを必要とする特権操作です。 まず、React アプリをサポートするサーバー側 API パーツを作成します。

Microsoft Graph を呼び出す OBO トークンを取得するユーティリティ メソッドを追加する

まず、既存の資格情報を使用して OAuth2 On-Behalf-Of フローを使用してトークンを取得するためのユーティリティ ファイルが必要です。

新しいファイル ./server/auth.ts を作成し、次のコードを追加します。

import { ConfidentialClientApplication } from "@azure/msal-node";
require('isomorphic-fetch');
import * as MSGraph from '@microsoft/microsoft-graph-client';
import * as Scopes from './common/scopes';

export const getGraphToken = async (confidentialClient: ConfidentialClientApplication, token: string): Promise<[boolean, string | any]> => {
  try {
    const graphTokenRequest = {
      oboAssertion: token,
      scopes: [
        Scopes.GRAPH_SITES_READ_ALL,
        Scopes.SPEMBEDDED_FILESTORAGECONTAINER_SELECTED
      ]
    };
    const oboGraphToken = (await confidentialClient.acquireTokenOnBehalfOf(graphTokenRequest))!.accessToken;
    return [true, oboGraphToken];
  } catch (error: any) {
    const errorResult = {
      status: 500,
      body: JSON.stringify({
        message: `Unable to generate Microsoft Graph OBO token: ${error.message}`,
        providedToken: token
      })
    };
    return [false, errorResult];
  }
}

これにより、構成された ConfidentialClientApplication とユーザーの ID トークンが取得され、MSAL ライブラリを使用して、Microsoft Graph の呼び出しに使用できる新しいトークンが要求されます。

サーバー側 API プロジェクトにコンテナー管理を追加する

次に、ハンドラーを作成して、Microsoft Graph を使用してコンテナーの一覧を取得し、React アプリに返します。 新しいファイル ./server/listContainers.ts を作成し、次のインポートを追加します。

import {
  Request,
  Response
} from "restify";
import * as MSAL from "@azure/msal-node";
require('isomorphic-fetch');
import * as MSGraph from '@microsoft/microsoft-graph-client';
import { getGraphToken } from "./auth";

次に、次のコードを追加して、OBO アクセス トークンの取得に使用される MSAL ConfidentialClientApplication のインスタンスを作成します。

const msalConfig: MSAL.Configuration = {
  auth: {
    clientId: process.env['API_ENTRA_APP_CLIENT_ID']!,
    authority: process.env['API_ENTRA_APP_AUTHORITY']!,
    clientSecret: process.env['API_ENTRA_APP_CLIENT_SECRET']!
  },
  system: {
    loggerOptions: {
      loggerCallback(loglevel: any, message: any, containsPii: any) {
        console.log(message);
      },
      piiLoggingEnabled: false,
      logLevel: MSAL.LogLevel.Verbose,
    }
  }
};

const confidentialClient = new MSAL.ConfidentialClientApplication(msalConfig);

次の操作を行う新しい関数を作成してエクスポートします。

  • 要求にアクセス トークンを Authorization 含むヘッダーが含まれていることを確認します。
  • そのトークンと を ConfidentialClientApplication 使用して、Microsoft Graph の呼び出しに使用できる OBO トークンを取得します。
  • OBO トークンを使用して、Microsoft Graph の呼び出しに使用する新しい AuthenticationProvider クライアントを作成します。
export const listContainers = async (req: Request, res: Response) => {
  if (!req.headers.authorization) {
    res.send(401, { message: 'No access token provided.' });
    return;
  }

  const [bearer, token] = (req.headers.authorization || '').split(' ');

  const [graphSuccess, oboGraphToken] = await getGraphToken(confidentialClient, token);

  if (!graphSuccess) {
    res.send(200, oboGraphToken);
    return;
  }

  const authProvider = (callback: MSGraph.AuthProviderCallback) => {
    callback(null, oboGraphToken);
  };
}

最後の手順では、Microsoft Graph クライアントを作成し、特定 ContainerTypeId のセットを持つすべてのコンテナーを要求します。 関数の終わり角かっこの前に、次のコードを即時に追加します。

try {
  const graphClient = MSGraph.Client.init({
    authProvider: authProvider,
    defaultVersion: 'beta'
  });

  const graphResponse = await graphClient.api(`storage/fileStorage/containers?$filter=containerTypeId eq ${process.env["CONTAINER_TYPE_ID"]}`).get();

  res.send(200, graphResponse);
  return;
} catch (error: any) {
  res.send(500, { message: `Unable to list containers: ${error.message}` });
  return;
}

この新しいエンドポイントを restify サーバーに追加します。 ./server/index.ts ファイルを見つけて開き、既存のインポートの最後に 1 つの import ステートメントを追加し、HTTP GET 要求のリスナーをエンドポイントに/api/listContainers追加します。

import { listContainers } from "./listContainers";
...

server.get('/api/listContainers', async (req, res, next) => {
  try {
    const response = await listContainers(req, res);
    res.send(200, response)
  } catch (error: any) {
    res.send(500, { message: `Error in API server: ${error.message}` });
  }
  next();
});

React プロジェクトを更新してコンテナーを表示する

サーバー側の API セットアップを使用すると、React プロジェクトを更新して、既存のコンテナーを選択するインターフェイスをユーザーに提供したり、新しいコンテナーを作成したりできます。

まず、新しいインターフェイス ./src/common/IContainer.ts を作成し、Microsoft Graph 呼び出しから送受信するオブジェクトを表す次の内容を指定します。

export interface IContainer {
  id: string;
  displayName: string;
  containerTypeId: string;
  createdDateTime: string;
}

API エンドポイントの呼び出しまたは React アプリから Microsoft Graph への直接呼び出しに使用される新しいサービスを作成します。 新しいファイル ./src/services/spembedded.ts を作成し、次のコードを追加します。

import { Providers, ProviderState } from '@microsoft/mgt-element';
import * as Msal from '@azure/msal-browser';
import * as Constants from './../common/constants';
import * as Scopes from './../common/scopes';
import { IContainer } from './../common/IContainer';

export default class SpEmbedded {
}

次に、ユーティリティ関数を追加して、Microsoft Graph の呼び出しに使用できるアクセス トークンを取得します。 これにより、サーバー側 API の呼び出しに送信するために使用する MSAL PublicClientApplication が作成されます。 SharePoint Embedded クラスに次の関数を追加します。

async getApiAccessToken() {
  const msalConfig: Msal.Configuration = {
    auth: {
      clientId: Constants.CLIENT_ENTRA_APP_CLIENT_ID,
      authority: Constants.CLIENT_ENTRA_APP_AUTHORITY,
    },
    cache: {
      cacheLocation: 'localStorage',
      storeAuthStateInCookie: false
    }
  };

  const scopes: Msal.SilentRequest = {
    scopes: [`api://${Constants.CLIENT_ENTRA_APP_CLIENT_ID}/${Scopes.SPEMBEDDED_CONTAINER_MANAGE}`],
    prompt: 'select_account',
    redirectUri: `${window.location.protocol}//${window.location.hostname}${(window.location.port === '80' || window.location.port === '443') ? '' : ':' + window.location.port}`
  };

  const publicClientApplication = new Msal.PublicClientApplication(msalConfig);
  await publicClientApplication.initialize();

  let tokenResponse;
  try {
    tokenResponse = await publicClientApplication.acquireTokenSilent(scopes);
    return tokenResponse.accessToken;
  } catch (error) {
    if (error instanceof Msal.InteractionRequiredAuthError) {
      tokenResponse = await publicClientApplication.acquireTokenPopup(scopes);
      return tokenResponse.accessToken;
    }
    console.log(error)
    return null;
  }
};

サーバー側 API を呼び出して、すべてのコンテナーの一覧を取得する次 listContainers() のメソッドを追加します。 これにより、ユーティリティ メソッドから返されるアクセス トークンが取得され、サーバー側 API の呼び出しに含まれます。 このアクセス トークンを使用して MSAL ConfidentialClientApplication を作成し、Microsoft Graph を呼び出す OBO トークンを取得することを思い出してください。 その OBO トークンには、サーバー側の呼び出しからのみ取得できるアクセス許可が付与されています。

async listContainers(): Promise<IContainer[] | undefined> {
  const api_endpoint = `${Constants.API_SERVER_URL}/api/listContainers`;

  if (Providers.globalProvider.state === ProviderState.SignedIn) {
    const token = await this.getApiAccessToken();
    const containerRequestHeaders = {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    };
    const containerRequestOptions = {
      method: 'GET',
      headers: containerRequestHeaders
    };
    const response = await fetch(api_endpoint, containerRequestOptions);

    if (response.ok) {
      const containerResponse = await response.json();
      return (containerResponse.value)
        ? (containerResponse.value) as IContainer[]
        : undefined;
    } else {
      console.error(`Unable to list Containers: ${JSON.stringify(response)}`);
      return undefined;
    }
  }
};

次に、すべてのコンテナー タスクと UI を処理する新しい React コンポーネントを作成します。 新しいファイル ./src/components/containers.tsx を作成し、次のコードを追加します。

import React, { useEffect, useState } from 'react';
import {
  Button,
  Dialog, DialogActions, DialogContent, DialogSurface, DialogBody, DialogTitle, DialogTrigger,
  Dropdown, Option,
  Input, InputProps, InputOnChangeData,
  Label,
  Spinner,
  makeStyles, shorthands, useId
} from '@fluentui/react-components';
import type {
  OptionOnSelectData,
  SelectionEvents
} from '@fluentui/react-combobox'
import { IContainer } from "./../common/IContainer";
import SpEmbedded from '../services/spembedded';

const spe = new SpEmbedded();

const useStyles = makeStyles({
  root: {
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    ...shorthands.padding('25px'),
  },
  containerSelector: {
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    rowGap: '10px',
    ...shorthands.padding('25px'),
  },
  containerSelectorControls: {
    width: '400px',
  },
  dialogContent: {
    display: 'flex',
    flexDirection: 'column',
    rowGap: '10px',
    marginBottom: '25px'
  }
});

export const Containers = (props: any) => {
  // BOOKMARK 1 - constants & hooks

  // BOOKMARK 2 - handlers go here

  // BOOKMARK 3 - component rendering
  return (
  );
}

export default Containers;

注:

コンポーネント内の // BOOKMARK # コメントに注目してください。 これらは、正しい場所にコードを追加していることを確認します。

最初の手順では、コンテナーの一覧を取得します。 まず、 の前 // BOOKMARK 1に次のコードを追加します。 これにより、サーバー側 API から取得されたコンテナーを保持するいくつかの状態値が設定されます。

const [containers, setContainers] = useState<IContainer[]>([]);
const [selectedContainer, setSelectedContainer] = useState<IContainer | undefined>(undefined);
const containerSelector = useId('containerSelector');

次に、次の React フックを後 // BOOKMARK 1 に追加して、ページが読み込まれたときにすべてのコンテナーを取得し、それらを追跡する状態オブジェクトを設定します。

useEffect(() => {
  (async () => {
    const containers = await spe.listContainers();
    if (containers) {
      setContainers(containers);
    }
  })();
}, []);

先ほど追加した React フック実装の後にイベント ハンドラーを作成します。これは、ユーザーが UX に追加するドロップダウン コントロールからコンテナーを選択したときに発生します。

const onContainerDropdownChange = (event: SelectionEvents, data: OptionOnSelectData) => {
  const selected = containers.find((container) => container.id === data.optionValue);
  setSelectedContainer(selected);
};

コードの後の メソッドに次を追加して、 return() レンダリングを更新します // BOOKMARK 3 。 これにより、コントロールとプレースホルダーが作成 DropDown されます。ここで、選択したコンテナーにコンテンツの一覧を追加します。

<div className={styles.root}>
  <div className={styles.containerSelector}>
    <Dropdown
      id={containerSelector}
      placeholder="Select a Storage Container"
      className={styles.containerSelectorControls}
      onOptionSelect={onContainerDropdownChange}>
      {containers.map((option) => (
        <Option key={option.id} value={option.id}>{option.displayName}</Option>
      ))}
    </Dropdown>
  </div>
  {selectedContainer && (`[[TOOD]] container "${selectedContainer.displayName}" contents go here`)}
</div>

このコードでは、最初にコンポーネントを作成したときにコンポーネントに追加したスタイル オブジェクトを使用します。 スタイルを使用するには、ステートメントとコメントの直前に次のコードをreturn()// BOOKMARK 3追加します。

const styles = useStyles();

最後の手順では、新しい Containersコンポーネントをアプリに追加します。 ファイルを ./src/App.tsx 見つけて開き、既存のインポートの後に次のインポートを追加します。

import Containers from "./components/containers";

コンポーネントreturn()<div></div> でマークアップを見つけます。 そのマークアップを次に置き換えて、ユーザーがサインインしている場合にのみコンポーネントを追加 Containers します。

<div>
  {isSignedIn && (<Containers />)}
</div>

テストの一覧とコンテナーの選択

次に、クライアント側の React アプリをテストして、React アプリのコンテナーの一覧表示と表示に対する変更の効果を確認しましょう。

プロジェクトのルート フォルダーのコマンド ラインから、次のコマンドを実行します。

npm run start

ブラウザーが読み込まれたら、使用しているのと同じ 職場および学校 アカウントを使用してサインインします。

サインイン後、ページが再読み込みされ、以前に作成したコンテナーの一覧が表示されます。

すべてのコンテナーを一覧表示する React アプリのスクリーンショット。

コンテナーを選択すると、条件付きロジックにコンテナーの名前が選択されたプレースホルダーが表示されます。

コンテナーを選択した後の React アプリのスクリーンショット。

コンソールで CtrlCキー + を押してサーバーを停止します。

新しいコンテナーを作成する機能を追加する

このセクションでは、サーバー側 API と React アプリを更新して、Web アプリケーションから新しい contains を作成します。

まず、React アプリをサポートするサーバー側 API パーツを作成します。

サーバー側 API プロジェクトにコンテナーを作成するためのサポートを追加する

次に、ハンドラーを作成し、Microsoft Graph を使用してコンテナーを作成し、React アプリに返します。 新しい ファイル ./server/createContainer.ts を作成し、次のインポートを追加します。

import {
  Request,
  Response
} from "restify";
import * as MSAL from "@azure/msal-node";
require('isomorphic-fetch');
import * as MSGraph from '@microsoft/microsoft-graph-client';
import { getGraphToken } from "./auth";

次に、次のコードを追加して、OBO アクセス トークンの取得に使用する MSAL ConfidentialClientApplication のインスタンスを作成します。

const msalConfig: MSAL.Configuration = {
  auth: {
    clientId: process.env['API_ENTRA_APP_CLIENT_ID']!,
    authority: process.env['API_ENTRA_APP_AUTHORITY']!,
    clientSecret: process.env['API_ENTRA_APP_CLIENT_SECRET']!
  },
  system: {
    loggerOptions: {
      loggerCallback(loglevel: any, message: any, containsPii: any) {
        console.log(message);
      },
      piiLoggingEnabled: false,
      logLevel: MSAL.LogLevel.Verbose,
    }
  }
};

const confidentialClient = new MSAL.ConfidentialClientApplication(msalConfig);

次の操作を行う新しい関数を作成してエクスポートします。

  • 要求にアクセス トークンを Authorization 含むヘッダーが含まれていることを確認します。
  • そのトークンと を ConfidentialClientApplication 使用して、Microsoft Graph の呼び出しに使用できる OBO トークンを取得します。
  • OBO トークンを使用して、Microsoft Graph の呼び出しに使用する新しい AuthenticationProvider クライアントを作成します。
export const createContainer = async (req: Request, res: Response) => {
  if (!req.headers.authorization) {
    res.send(401, { message: 'No access token provided.' });
    return;
  }

  const [bearer, token] = (req.headers.authorization || '').split(' ');

  const [graphSuccess, graphTokenRequest] = await getGraphToken(confidentialClient, token);

  if (!graphSuccess) {
    res.send(200, graphTokenRequest);
    return;
  }

  const authProvider = (callback: MSGraph.AuthProviderCallback) => {
    callback(null, graphTokenRequest);
  };
}

最後の手順では、Microsoft Graph クライアントを作成し、特定 ContainerTypeIdの に設定された新しいコンテナーを作成する要求を送信します。 関数の終わり角かっこの前に、次のコードを即時に追加します。

try {
  const graphClient = MSGraph.Client.init({
    authProvider: authProvider,
    defaultVersion: 'beta'
  });

  const containerRequestData = {
    displayName: req.body!.displayName,
    description: (req.body?.description) ? req.body.description : '',
    containerTypeId: process.env["CONTAINER_TYPE_ID"]
  };

  const graphResponse = await graphClient.api(`storage/fileStorage/containers`).post(containerRequestData);

  res.send(200, graphResponse);
  return;
} catch (error: any) {
  res.send(500, { message: `Failed to create container: ${error.message}` });
  return;
}

この新しいエンドポイントを restify サーバーに追加します。 ./server/index.ts ファイルを見つけて開き、既存のインポートの末尾に 1 つの import ステートメントを追加し、HTTP POST 要求のリスナーをエンドポイントに/api/createContainers追加します。

import { createContainer } from "./createContainer";

...

server.post('/api/createContainer', async (req, res, next) => {
  try {
    const response = await createContainer(req, res);
    res.send(200, response)
  } catch (error: any) {
    res.send(500, { message: `Error in API server: ${error.message}` });
  }
  next();
});

React プロジェクトを更新して新しいコンテナーを作成する

ファイル ./src/services/spembedded.ts を見つけて開き、クラスに次のコードを追加します。

async createContainer(containerName: string, containerDescription: string = ''): Promise<IContainer | undefined> {
  const api_endpoint = `${Constants.API_SERVER_URL}/api/createContainer`;

  if (Providers.globalProvider.state === ProviderState.SignedIn) {
    const token = await this.getApiAccessToken();
    const containerRequestHeaders = {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    };

    const containerRequestData = {
      displayName: containerName,
      description: containerDescription
    };
    const containerRequestOptions = {
      method: 'POST',
      headers: containerRequestHeaders,
      body: JSON.stringify(containerRequestData)
    };

    const response = await fetch(api_endpoint, containerRequestOptions);

    if (response.ok) {
      const containerResponse = await response.json();
      return containerResponse as IContainer;
    } else {
      console.error(`Unable to create container: ${JSON.stringify(response)}`);
      return undefined;
    }
  }
};

この新しいメソッドは既存 listContainers() のメソッドに似ていますが、新しいオブジェクトを作成し、POST としてサーバー側 API に送信する場合を除きます。

最後の手順では、コンテナーの作成をサポートするように UI を更新するようにコンポーネントを更新 Containers します。 ./src/components/containers.tsx ファイルを見つけて開きます。

この手順では、Fluent UI React Dialog コンポーネントを使用します。 まず、コメントの直前に次の状態オブジェクトと UI コンポーネント ID オブジェクトを // BOOKMARK 1 追加します。

const [dialogOpen, setDialogOpen] = useState(false);
const containerName = useId('containerName');
const [name, setName] = useState('');
const containerDescription = useId('containerDescription');
const [description, setDescription] = useState('');
const [creatingContainer, setCreatingContainer] = useState(false);

次に、コメントの直前に次のコードを // BOOKMARK 2 追加します。 これらのハンドラーは、 内にあるコンポーネントから新しい Container の名前と説明のプロパティをInputDialog更新するために使用されます。 また、ユーザーがコンテナーを作成するためのボタンを選択して処理するためにも使用されます。

const handleNameChange: InputProps["onChange"] = (event: React.ChangeEvent<HTMLInputElement>, data: InputOnChangeData): void => {
  setName(data?.value);
};

const handleDescriptionChange: InputProps["onChange"] = (event: React.ChangeEvent<HTMLInputElement>, data: InputOnChangeData): void => {
  setDescription(data?.value);
};

const onContainerCreateClick = async (event: React.MouseEvent<HTMLButtonElement>): Promise<void> => {
  setCreatingContainer(true);
  const newContainer = await spe.createContainer(name, description);

  if (newContainer) {
    setName('');
    setDescription('');
    setContainers(current => [...current, newContainer]);
    setSelectedContainer(newContainer);
    setDialogOpen(false);
  } else {
    setName('');
    setDescription('');
  }
  setCreatingContainer(false);
}

最後に、終了 </Dropdown> 要素の直後に次の React コードを追加します。 これにより、ボタンからトリガーされる Fluent UI React Dialog コンポーネントが作成されます。

<Dialog open={dialogOpen} onOpenChange={(event, data) => setDialogOpen(data.open)}>

  <DialogTrigger disableButtonEnhancement>
    <Button className={styles.containerSelectorControls} appearance='primary'>Create a new storage Container</Button>
  </DialogTrigger>

  <DialogSurface>
    <DialogBody>
      <DialogTitle>Create a new storage Container</DialogTitle>

      <DialogContent className={styles.dialogContent}>
        <Label htmlFor={containerName}>Container name:</Label>
        <Input id={containerName} className={styles.containerSelectorControls} autoFocus required
          value={name} onChange={handleNameChange}></Input>
        <Label htmlFor={containerDescription}>Container description:</Label>
        <Input id={containerDescription} className={styles.containerSelectorControls} autoFocus required
          value={description} onChange={handleDescriptionChange}></Input>
        {creatingContainer &&
          <Spinner size='medium' label='Creating storage Container...' labelPosition='after' />
        }
      </DialogContent>

      <DialogActions>
        <DialogTrigger disableButtonEnhancement>
          <Button appearance="secondary" disabled={creatingContainer}>Cancel</Button>
        </DialogTrigger>
        <Button appearance="primary"
          value={name}
          onClick={onContainerCreateClick}
          disabled={creatingContainer || (name === '')}>Create storage Container</Button>
      </DialogActions>
    </DialogBody>
  </DialogSurface>

</Dialog>

新しいコンテナーの作成をテストする

次に、クライアント側の React アプリをテストして、React アプリでコンテナーを作成するための変更の効果を確認しましょう。

プロジェクトのルート フォルダーのコマンド ラインから、次のコマンドを実行します。

npm run start

ブラウザーが読み込まれたら、使用しているのと同じ 職場および学校 アカウントを使用してサインインします。

サインイン後、ページが再読み込みされ、ダイアログを起動するためのボタンが含まれるようになります。

ダイアログを起動するボタンを含む React アプリのスクリーンショット。

[ 新しいストレージ コンテナーの作成] ボタンを選択してダイアログを開きます。 名前を入力するまでボタンが無効になっていることに注目してください。

新しいコンテナーを作成するためのダイアログのスクリーンショット。

コンテナーの名前と説明を入力し、[ ストレージ コンテナーの作成] を選択します。 コンテナーの作成中、ボタンは無効になり、 Spinner コントロールに動作しているユーザーが表示されます。

コンテナーを作成しているダイアログのスクリーンショット。

ダイアログが消えると、セレクターに新しいコンテナーが表示されます。

新しいコンテナーを使用して更新されたセレクターを示すスクリーンショット。

コンソールで CtrlCキー + を押してサーバーを停止します。

概要

この演習では、既存のプロジェクトを更新して SharePoint Embedded Containers を作成および取得しました。

自分の知識をチェックする

1.

サーバー側 API の ConfidentialClientApplication の目的は何ですか?

2.

React アプリケーションで直接実行できない操作と、サーバー側 API が必要な操作はどれですか?