Skip to content
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
48 changes: 29 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,22 +180,26 @@ by using the component attributes mapped to the corresponding methods of

#### Attributes

| Name | Type | Description |
| ---------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `app-id` | `string` | The Google Drive app ID. See [`PickerBuilder.setAppId`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setappid). |
| `client-id` | `string` | The OAuth 2.0 client ID. See [Using OAuth 2.0 to Access Google APIs](https://developers.google.com/identity/protocols/oauth2). |
| `developer-key` | `string` | The API key for accessing Google Picker API. See [`PickerBuilder.setDeveloperKey`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setdeveloperkey). |
| `hide-title-bar` | `"default"\|"true"\|"false"` | Hides the title bar of the picker if set to true. See [`PickerBuilder.hideTitleBar`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.hidetitlebar). |
| `locale` | `string` | The locale to use for the picker. See [`PickerBuilder.setLocale`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setlocale). |
| `max-items` | `number` | The maximum number of items that can be selected. See [`PickerBuilder.setMaxItems`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setmaxitems). |
| `mine-only` | `boolean` | If set to true, only shows files owned by the user. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
| `multiselect` | `boolean` | Enables multiple file selection if set to true. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
| `nav-hidden` | `boolean` | Hides the navigation pane if set to true. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
| `oauth-token` | `string` | The OAuth 2.0 token for authentication. See [`PickerBuilder.setOAuthToken`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setoauthtoken). |
| `origin` | `string` | The origin parameter for the picker. See [`PickerBuilder.setOrigin`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setorigin). |
| `relay-url` | `string` | The relay URL for the picker. See [`PickerBuilder.setRelayUrl`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setrelayurl). |
| `scope` | `string` | The OAuth 2.0 scope for the picker. The default is `https://www.googleapis.com/auth/drive.file`. See [Drive API scopes](https://developers.google.com/drive/api/guides/api-specific-auth#drive-scopes). |
| `title` | `string` | The title of the picker. See [`PickerBuilder.setTitle`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.settitle). |
| Name | Type | Description |
| ------------------------ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `app-id` | `string` | The Google Drive app ID. See [`PickerBuilder.setAppId`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setappid). |
| `client-id` | `string` | The OAuth 2.0 client ID. See [Using OAuth 2.0 to Access Google APIs](https://developers.google.com/identity/protocols/oauth2). |
| `developer-key` | `string` | The API key for accessing Google Picker API. See [`PickerBuilder.setDeveloperKey`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setdeveloperkey). |
| `hide-title-bar` | `"default"\|"true"\|"false"` | Hides the title bar of the picker if set to true. See [`PickerBuilder.hideTitleBar`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.hidetitlebar). |
| `locale` | `string` | The locale to use for the picker. See [`PickerBuilder.setLocale`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setlocale). |
| `max-items` | `number` | The maximum number of items that can be selected. See [`PickerBuilder.setMaxItems`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setmaxitems). |
| `mine-only` | `boolean` | If set to true, only shows files owned by the user. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
| `multiselect` | `boolean` | Enables multiple file selection if set to true. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
| `nav-hidden` | `boolean` | Hides the navigation pane if set to true. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature). |
| `oauth-token` | `string` | The OAuth 2.0 token for authentication. See [`PickerBuilder.setOAuthToken`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setoauthtoken). |
| `origin` | `string` | The origin parameter for the picker. See [`PickerBuilder.setOrigin`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setorigin). |
| `relay-url` | `string` | The relay URL for the picker. See [`PickerBuilder.setRelayUrl`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setrelayurl). |
| `scope` | `string` | The OAuth 2.0 scope for the picker. The default is `https://www.googleapis.com/auth/drive.file`. See [Drive API scopes](https://developers.google.com/drive/api/guides/api-specific-auth#drive-scopes). |
| `title` | `string` | The title of the picker. See [`PickerBuilder.setTitle`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.settitle). |
| `hd` | `string` | The hosted domain to restrict sign-in to. (Optional) See the `hd` field in the OpenID Connect docs. |
| `include-granted-scopes` | `boolean` | Enables applications to use incremental authorization. See [`TokenClientConfig.include_granted_scopes`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig). |
| `login-hint` | `string` | An email address or an ID token 'sub' value. Google will use the value as a hint of which user to sign in. See the `login_hint` field in the OpenID Connect docs. |
| `prompt` | `""\|"none"\|"consent"\|"select_account"` | A space-delimited, case-sensitive list of prompts to present the user. See [`TokenClientConfig.prompt`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig) |

#### Events

Expand All @@ -215,9 +219,15 @@ by using the component attributes mapped to the corresponding methods of

#### Properties

| Name | Type | Description |
| --------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `visible` | `boolean` | Controls the visibility of the picker after the picker dialog has been
closed. If any of the attributes change, the picker will be rebuilt and
the visibility will be reset. |
| Name | Type | Description |
| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `visible` | `boolean` | Controls the visibility of the picker after the picker dialog has been
closed. If any of the attributes change, the picker will be rebuilt and
the visibility will be reset. |
| `tokenClientConfig` | \`Omit< | |

```
google.accounts.oauth2.TokenClientConfig,
"callback" \| "error_callback" >` | |
```

### `<drive-picker-docs-view/>`

Expand Down
39 changes: 38 additions & 1 deletion custom-elements.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,15 @@
"privacy": "public",
"description": "Controls the visibility of the picker after the picker dialog has been\nclosed. If any of the attributes change, the picker will be rebuilt and\nthe visibility will be reset."
},
{
"kind": "field",
"name": "tokenClientConfig",
"type": {
"text": "Omit<\n\t\tgoogle.accounts.oauth2.TokenClientConfig,\n\t\t\"callback\" | \"error_callback\"\n\t>"
},
"privacy": "public",
"readonly": true
},
{
"kind": "method",
"name": "build",
Expand Down Expand Up @@ -251,7 +260,7 @@
},
{
"kind": "method",
"name": "retrieveAccessToken",
"name": "requestAccessToken",
"privacy": "private",
"return": {
"type": {
Expand Down Expand Up @@ -407,6 +416,34 @@
"text": "string"
},
"description": "The title of the picker. See [`PickerBuilder.setTitle`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.settitle)."
},
{
"type": {
"text": "string"
},
"description": "The hosted domain to restrict sign-in to. (Optional) See the `hd` field in the OpenID Connect docs.",
"name": "hd"
},
{
"type": {
"text": "boolean"
},
"description": "Enables applications to use incremental authorization. See [`TokenClientConfig.include_granted_scopes`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig).",
"name": "include-granted-scopes"
},
{
"type": {
"text": "string"
},
"description": "An email address or an ID token 'sub' value. Google will use the value as a hint of which user to sign in. See the `login_hint` field in the OpenID Connect docs.",
"name": "login-hint"
},
{
"type": {
"text": "\"\"|\"none\"|\"consent\"|\"select_account\""
},
"description": "A space-delimited, case-sensitive list of prompts to present the user. See [`TokenClientConfig.prompt`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig)",
"name": "prompt"
}
],
"superclass": {
Expand Down
43 changes: 34 additions & 9 deletions src/drive-picker/drive-picker-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
getBoolAttr,
getNumberAttribute,
loadApi,
retrieveAccessToken,
requestAccessToken,
setBoolAttrWithDefault,
} from "../utils";

Expand Down Expand Up @@ -77,6 +77,10 @@ declare global {
* @attr {string} relay-url - The relay URL for the picker. See [`PickerBuilder.setRelayUrl`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setrelayurl).
* @attr {string} scope - The OAuth 2.0 scope for the picker. The default is `https://www.googleapis.com/auth/drive.file`. See [Drive API scopes](https://developers.google.com/drive/api/guides/api-specific-auth#drive-scopes).
* @attr {string} title - The title of the picker. See [`PickerBuilder.setTitle`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.settitle).
* @attr {string} hd - The hosted domain to restrict sign-in to. (Optional) See the `hd` field in the OpenID Connect docs.
* @attr {boolean} include-granted-scopes - Enables applications to use incremental authorization. See [`TokenClientConfig.include_granted_scopes`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig).
* @attr {string} login-hint - An email address or an ID token 'sub' value. Google will use the value as a hint of which user to sign in. See the `login_hint` field in the OpenID Connect docs.
* @attr {""|"none"|"consent"|"select_account"} prompt - A space-delimited, case-sensitive list of prompts to present the user. See [`TokenClientConfig.prompt`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig)
*
* @example
*
Expand Down Expand Up @@ -130,6 +134,32 @@ export class DrivePickerElement extends HTMLElement {
this.picker?.setVisible(value);
}

public get tokenClientConfig(): Omit<
google.accounts.oauth2.TokenClientConfig,
"callback" | "error_callback"
> {
const clientId = this.getAttribute("client-id");
const scope =
this.getAttribute("scope") ??
"https://www.googleapis.com/auth/drive.file";

if (!clientId || !scope) {
throw new Error("client-id and scope are required attributes");
}

return {
client_id: clientId,
hd: this.getAttribute("hd") ?? undefined,
include_granted_scopes: Boolean(
this.getAttribute("include-granted-scope"),
),
login_hint: this.getAttribute("login-hint") ?? undefined,
prompt: (this.getAttribute("prompt") ??
"") as google.accounts.oauth2.TokenClientConfig["prompt"],
scope,
};
}

attributeChangedCallback() {
this.build();
return;
Expand Down Expand Up @@ -181,7 +211,7 @@ export class DrivePickerElement extends HTMLElement {

// OAuth token is required either as an attribute or from the OAuth flow using the client ID and scope
const oauthToken =
this.getAttribute("oauth-token") ?? (await this.retrieveAccessToken());
this.getAttribute("oauth-token") ?? (await this.requestAccessToken());

if (!oauthToken) return;

Expand Down Expand Up @@ -269,13 +299,8 @@ export class DrivePickerElement extends HTMLElement {
);
}

private retrieveAccessToken(): Promise<string | undefined> {
return retrieveAccessToken(
// biome-ignore lint/style/noNonNullAssertion: just let the error bubble up when null
this.getAttribute("client-id")!,
this.getAttribute("scope") ??
"https://www.googleapis.com/auth/drive.file",
)
private async requestAccessToken(): Promise<string | undefined> {
return requestAccessToken(this.tokenClientConfig)
.then((response) => {
const { access_token: token } = response;
if (!token) {
Expand Down
30 changes: 25 additions & 5 deletions src/stories/utils/manifest-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,32 @@ export const elementArgTypes: {
case "number":
inputType.control = "number";
break;
case '"default"|"true"|"false"':
inputType.control = "select";
inputType.options = ["default", "true", "false"];
break;
default:
throw new Error(`Unsupported type: ${attr.type?.text}`);
// check if string literal union such as "default"|"true"|"false"
if (
attr.type?.text.match(/"[a-zA-Z-0-9-_"]*"/) &&
attr.type?.text.includes("|")
) {
inputType.control = "select";
inputType.options = attr.type?.text.replace(/"/g, "").split("|");
} else {
throw new Error(`Unsupported type: ${attr.type?.text}`);
}
}

if (
tagName === "drive-picker" &&
[
"client-id",
"scope",
"hd",
"prompt",
"login-hint",
"include-granted-scopes",
].includes(attr.name)
) {
// biome-ignore lint/style/noNonNullAssertion: <explanation>
inputType!.table!.category = "OAuth";
}

return [attr.name, inputType];
Expand Down
11 changes: 6 additions & 5 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,18 @@ export class ClientConfigError extends Error {
}
}

export async function retrieveAccessToken(
clientId: string,
scope: string,
export async function requestAccessToken(
tokenClientConfig: Omit<
google.accounts.oauth2.TokenClientConfig,
"callback" | "error_callback"
>,
): Promise<google.accounts.oauth2.TokenResponse> {
if (!window.google?.accounts?.oauth2) {
await injectScript(GSI_URL);
}
return new Promise((resolve, reject) => {
const client = window.google.accounts.oauth2.initTokenClient({
client_id: clientId,
scope: scope.replace(/,/g, " ").replace(/\s+/g, " "),
...tokenClientConfig,
callback: resolve,
error_callback: reject,
});
Expand Down
Loading