Skip to content

Commit ec9f8ea

Browse files
authored
fix: export prop types from manifest/jsdoc (#52)
1 parent d3825f0 commit ec9f8ea

File tree

8 files changed

+298
-19
lines changed

8 files changed

+298
-19
lines changed

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ See the framework specific demos:
2727
- [Listening to Events](#listening-to-events)
2828
- [Event Details](#event-details)
2929
- [Controlling Visibility](#controlling-visibility)
30+
- [React and JSX](#react-and-jsx)
3031
- [Support](#support)
3132
- [Reference](#reference)
3233
- [`<drive-picker/>`](#drive-picker)
@@ -162,6 +163,37 @@ To make the picker visible, set the `visible` property of the `drive-picker` ele
162163

163164
After the picker dialog has been closed, the `visible` property will be reset to `false`.
164165

166+
### React and JSX
167+
168+
To use the component in a React application, you can extend the global `JSX` namespace as follows:
169+
170+
```ts
171+
import type {
172+
DrivePickerElement,
173+
DrivePickerDocsViewElement,
174+
DrivePickerElementProps,
175+
DrivePickerDocsViewElementProps,
176+
} from "@googleworkspace/drive-picker-element";
177+
178+
declare global {
179+
namespace JSX {
180+
interface IntrinsicElements {
181+
"drive-picker": React.DetailedHTMLProps<
182+
React.HTMLAttributes<DrivePickerElement> & DrivePickerElementProps,
183+
DrivePickerElement
184+
>;
185+
"drive-picker-docs-view": React.DetailedHTMLProps<
186+
React.HTMLAttributes<DrivePickerDocsViewElement> &
187+
DrivePickerDocsViewElementProps,
188+
DrivePickerDocsViewElement
189+
>;
190+
}
191+
}
192+
}
193+
```
194+
195+
The above snippet can be added to a declaration file (e.g. `app.d.ts`) in your React project.
196+
165197
## Support
166198

167199
To report issues or feature requests for the underlying Drive Picker, please use the [Google Picker issue tracker](https://developers.google.com/drive/picker/support#developer_product_feedback). For all other issues, please use the [GitHub issue tracker](https://github.com/googleworkspace/drive-picker-element/issues).

custom-elements.json

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@
3838
"name": "DrivePickerDocsViewElement",
3939
"module": "src/index.ts"
4040
}
41+
},
42+
{
43+
"kind": "js",
44+
"name": "DrivePickerElementProps",
45+
"declaration": {
46+
"name": "DrivePickerElementProps",
47+
"module": "src/index.ts"
48+
}
49+
},
50+
{
51+
"kind": "js",
52+
"name": "DrivePickerDocsViewElementProps",
53+
"declaration": {
54+
"name": "DrivePickerDocsViewElementProps",
55+
"module": "src/index.ts"
56+
}
4157
}
4258
]
4359
},
@@ -484,12 +500,28 @@
484500
"name": "DrivePickerDocsViewElement",
485501
"module": "\"./drive-picker-docs-view-element\""
486502
}
503+
},
504+
{
505+
"kind": "js",
506+
"name": "DrivePickerDocsViewElementProps",
507+
"declaration": {
508+
"name": "DrivePickerDocsViewElementProps",
509+
"module": "\"./props\""
510+
}
511+
},
512+
{
513+
"kind": "js",
514+
"name": "DrivePickerElementProps",
515+
"declaration": {
516+
"name": "DrivePickerElementProps",
517+
"module": "\"./props\""
518+
}
487519
}
488520
]
489521
},
490522
{
491523
"kind": "javascript-module",
492-
"path": "src/drive-picker/types.ts",
524+
"path": "src/drive-picker/props.ts",
493525
"declarations": [],
494526
"exports": []
495527
}

package.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@
9494
"build:components",
9595
"build:manifest",
9696
"build:readme",
97-
"build:storybook"
97+
"build:storybook",
98+
"build:props"
9899
]
99100
},
100101
"build:components": {
@@ -107,6 +108,20 @@
107108
"dist/**/*.(js|ts|map)"
108109
]
109110
},
111+
"build:props": {
112+
"clean": false,
113+
"command": "tsx ./scripts/props.ts",
114+
"files": [
115+
"custom-elements.json",
116+
"scripts/props.ts"
117+
],
118+
"output": [
119+
"src/drive-picker/props.ts"
120+
],
121+
"dependencies": [
122+
"build:manifest"
123+
]
124+
},
110125
"build:manifest": {
111126
"command": "cem analyze --config cem.config.js && biome check --fix custom-elements.json",
112127
"files": [

scripts/props.ts

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/**
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import fs from "node:fs";
18+
import prettier from "prettier";
19+
import ts, { factory } from "typescript";
20+
21+
import type {
22+
CustomElementDeclaration,
23+
Declaration,
24+
Package,
25+
} from "custom-elements-manifest/schema";
26+
27+
const manifest = JSON.parse(
28+
fs.readFileSync("custom-elements.json", "utf-8"),
29+
) as Package;
30+
31+
function isCustomElementDeclaration(
32+
declaration?: Declaration,
33+
): declaration is CustomElementDeclaration {
34+
return (declaration as CustomElementDeclaration)?.customElement === true;
35+
}
36+
37+
const customElements = manifest.modules
38+
.flatMap((module) => module.declarations)
39+
.filter(isCustomElementDeclaration)
40+
.sort((a, b) => a.tagName?.localeCompare(b.tagName ?? "") ?? 0);
41+
42+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
43+
const resultFile = ts.createSourceFile(
44+
"props.ts",
45+
"",
46+
ts.ScriptTarget.Latest,
47+
false,
48+
ts.ScriptKind.TS,
49+
);
50+
51+
const statements: ts.Statement[] = [];
52+
53+
for (const element of customElements) {
54+
const properties: ts.PropertySignature[] = [];
55+
56+
for (const attribute of element.attributes ?? []) {
57+
// Create the property signature
58+
const prop = factory.createPropertySignature(
59+
undefined, // No modifiers
60+
attribute.name.includes("-")
61+
? factory.createStringLiteral(`${attribute.name}`)
62+
: factory.createIdentifier(attribute.name),
63+
factory.createToken(ts.SyntaxKind.QuestionToken), // Make props optional
64+
factory.createTypeReferenceNode(
65+
attribute.type?.text ?? "unknown",
66+
undefined,
67+
),
68+
);
69+
70+
if (attribute.description) {
71+
const jsDocText = `* ${attribute.description.replace(/\n/g, "\n * ")}`;
72+
ts.addSyntheticLeadingComment(
73+
prop,
74+
ts.SyntaxKind.MultiLineCommentTrivia,
75+
jsDocText,
76+
true,
77+
);
78+
}
79+
80+
properties.push(prop);
81+
}
82+
83+
const propsInterface = factory.createInterfaceDeclaration(
84+
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
85+
factory.createIdentifier(`${element.name}Props`),
86+
undefined,
87+
undefined,
88+
properties,
89+
);
90+
91+
statements.push(propsInterface);
92+
}
93+
94+
const outputFile = "src/drive-picker/props.ts";
95+
const resultText = await prettier.format(
96+
printer.printList(
97+
ts.ListFormat.MultiLine,
98+
factory.createNodeArray(statements),
99+
resultFile,
100+
),
101+
{
102+
parser: "typescript",
103+
useTabs: true,
104+
},
105+
);
106+
107+
const licenseHeader = `/**
108+
* Copyright 2025 Google LLC
109+
*
110+
* Licensed under the Apache License, Version 2.0 (the "License");
111+
* you may not use this file except in compliance with the License.
112+
* You may obtain a copy of the License at
113+
*
114+
* http://www.apache.org/licenses/LICENSE-2.0
115+
*
116+
* Unless required by applicable law or agreed to in writing, software
117+
* distributed under the License is distributed on an "AS IS" BASIS,
118+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
119+
* See the License for the specific language governing permissions and
120+
* limitations under the License.
121+
*/`;
122+
123+
fs.writeFileSync(outputFile, `${licenseHeader}\n\n${resultText}`, "utf8");
124+
console.log(`Generated Drive Picker prop type definitions at ${outputFile}`);

src/drive-picker/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@
1717
export { DrivePickerElement } from "./drive-picker-element";
1818

1919
export { DrivePickerDocsViewElement } from "./drive-picker-docs-view-element";
20+
21+
export type {
22+
DrivePickerDocsViewElementProps,
23+
DrivePickerElementProps,
24+
} from "./props";

src/drive-picker/props.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export interface DrivePickerElementProps {
18+
/** The Google Drive app ID. See [`PickerBuilder.setAppId`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setappid).*/
19+
"app-id"?: string;
20+
/** The OAuth 2.0 client ID. See [Using OAuth 2.0 to Access Google APIs](https://developers.google.com/identity/protocols/oauth2).*/
21+
"client-id"?: string;
22+
/** The API key for accessing Google Picker API. See [`PickerBuilder.setDeveloperKey`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setdeveloperkey).*/
23+
"developer-key"?: string;
24+
/** Hides the title bar of the picker if set to true. See [`PickerBuilder.hideTitleBar`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.hidetitlebar).*/
25+
"hide-title-bar"?: "default" | "true" | "false";
26+
/** The locale to use for the picker. See [`PickerBuilder.setLocale`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setlocale).*/
27+
locale?: string;
28+
/** The maximum number of items that can be selected. See [`PickerBuilder.setMaxItems`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setmaxitems).*/
29+
"max-items"?: number;
30+
/** If set to true, only shows files owned by the user. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature).*/
31+
"mine-only"?: boolean;
32+
/** Enables multiple file selection if set to true. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature).*/
33+
multiselect?: boolean;
34+
/** Hides the navigation pane if set to true. See [`PickerBuilder.enableFeature`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.enablefeature).*/
35+
"nav-hidden"?: boolean;
36+
/** The OAuth 2.0 token for authentication. See [`PickerBuilder.setOAuthToken`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setoauthtoken).*/
37+
"oauth-token"?: string;
38+
/** The origin parameter for the picker. See [`PickerBuilder.setOrigin`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setorigin).*/
39+
origin?: string;
40+
/** The relay URL for the picker. See [`PickerBuilder.setRelayUrl`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.setrelayurl).*/
41+
"relay-url"?: string;
42+
/** 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).*/
43+
scope?: string;
44+
/** The title of the picker. See [`PickerBuilder.setTitle`](https://developers.google.com/drive/picker/reference/picker.pickerbuilder.settitle).*/
45+
title?: string;
46+
/** The hosted domain to restrict sign-in to. (Optional) See the `hd` field in the OpenID Connect docs.*/
47+
hd?: string;
48+
/** Enables applications to use incremental authorization. See [`TokenClientConfig.include_granted_scopes`](https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig).*/
49+
"include-granted-scopes"?: boolean;
50+
/** 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.*/
51+
"login-hint"?: string;
52+
/** 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)*/
53+
prompt?: "" | "none" | "consent" | "select_account";
54+
}
55+
export interface DrivePickerDocsViewElementProps {
56+
/** Whether to allow the user to select files from shared drives. See [`DocsView.enableDrives`](https://developers.google.com/drive/picker/reference/picker.docsview.setenabledrives).*/
57+
"enable-drives"?: "default" | "true" | "false";
58+
/** Whether to include folders in the view. See [`DocsView.includeFolders`](https://developers.google.com/drive/picker/reference/picker.docsview.setincludefolders).*/
59+
"include-folders"?: "default" | "true" | "false";
60+
/** A comma-separated list of MIME types to filter the view. See [`View.setMimeTypes`](https://developers.google.com/drive/picker/reference/picker.view.setmimetypes).*/
61+
"mime-types"?: string;
62+
/** The mode of the view. See [`DocsViewMode`](https://developers.google.com/drive/picker/reference/picker.docsviewmode).*/
63+
mode?: string;
64+
/** Whether to show files owned by the user. See [`DocsView.ownedByMe`](https://developers.google.com/drive/picker/reference/picker.docsview.setownedbyme).*/
65+
"owned-by-me"?: "default" | "true" | "false";
66+
/** The ID of the folder to view. See [`DocsView.setParent`](https://developers.google.com/drive/picker/reference/picker.docsview.setparent).*/
67+
parent?: string;
68+
/** The query string to filter the view. See [`View.setQuery`](https://developers.google.com/drive/picker/reference/picker.view.setquery).*/
69+
query?: string;
70+
/** Whether to allow the user to select folders. See [`DocsView.selectFolderEnabled`](https://developers.google.com/drive/picker/reference/picker.docsview.setselectfolderenabled).*/
71+
"select-folder-enabled"?: "default" | "true" | "false";
72+
/** Whether to show starred files. See [`DocsView.starred`](https://developers.google.com/drive/picker/reference/picker.docsview.setstarred).*/
73+
starred?: "default" | "true" | "false";
74+
/** The `keyof typeof google.picker.ViewId`. For example, `"DOCS"`, which is equivalent to `google.picker.ViewId.DOCS`. See [`ViewId`](https://developers.google.com/drive/picker/reference/picker.viewid).*/
75+
"view-id"?: string;
76+
}

src/drive-picker/types.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { DrivePickerDocsViewElement, DrivePickerElement } from "./drive-picker";
17+
import {
18+
DrivePickerDocsViewElement,
19+
type DrivePickerDocsViewElementProps,
20+
DrivePickerElement,
21+
type DrivePickerElementProps,
22+
} from "./drive-picker";
1823

1924
customElements.define("drive-picker", DrivePickerElement);
2025
customElements.define("drive-picker-docs-view", DrivePickerDocsViewElement);
@@ -28,4 +33,9 @@ declare global {
2833
}
2934
}
3035

31-
export type { DrivePickerElement, DrivePickerDocsViewElement };
36+
export type {
37+
DrivePickerElement,
38+
DrivePickerDocsViewElement,
39+
DrivePickerElementProps,
40+
DrivePickerDocsViewElementProps,
41+
};

0 commit comments

Comments
 (0)