diff --git a/plugins/stack-overflow/.eslintrc.js b/plugins/stack-overflow/.eslintrc.js
new file mode 100644
index 0000000000..13573efa9c
--- /dev/null
+++ b/plugins/stack-overflow/.eslintrc.js
@@ -0,0 +1,3 @@
+module.exports = {
+ extends: [require.resolve('@backstage/cli/config/eslint')],
+};
diff --git a/plugins/stack-overflow/README.md b/plugins/stack-overflow/README.md
new file mode 100644
index 0000000000..cea63c41c4
--- /dev/null
+++ b/plugins/stack-overflow/README.md
@@ -0,0 +1,73 @@
+# Stack Overflow
+
+A plugin that provides stack overflow specific functionality that can be used in different ways (e.g. for homepage and search) to compose your Backstage App.
+
+## Getting started
+
+Before we begin, make sure:
+
+- You have created your own standalone Backstage app using @backstage/create-app and not using a fork of the backstage repository. If you haven't setup Backstage already, start [here](https://backstage.io/docs/getting-started/).
+
+To use any of the functionality this plugin provides, you need to start by configuring your App with the following config:
+
+```yaml
+stackoverflow:
+ baseUrl: https://api.stackexchange.com/2.2 # alternative: your internal stack overflow instance
+```
+
+## Areas of Responsibility
+
+This search plugin is primarily responsible for the following:
+
+- Exposing various stack-overflow related components like `` which can be used for composing the searchpage, and `` which can be used for composing the homepage.
+- Provides a `StackOverflowQuestionsCollator`, which can be used in the search backend to index stack overflow questions to your Backstage Search.
+
+### Index Stack Overflow Questions to search
+
+Before you are able to start index stack overflow questions to search, you need to go through the [search getting started guide](https://backstage.io/docs/features/search/getting-started).
+
+When you have your `packages/backend/src/plugins/search.ts` file ready to make modifications, add the following code snippet to add the `StackOverflowQuestionsCollator`. Note that you can modify the request params.
+
+```ts
+indexBuilder.addCollator({
+ defaultRefreshIntervalSeconds: 60,
+ collator: new StackOverflowQuestionsCollator({
+ config,
+ requestParams: {
+ tagged: ['backstage'],
+ site: 'stackoverflow',
+ pagesize: 100,
+ },
+ }),
+});
+```
+
+#### Use specific search result list item for Stack Overflow Question
+
+When you have your `packages/app/src/components/search/SearchPage.tsx` file ready to make modifications, add the following code snippet to add the `StackOverflowSearchResultListItem` when the type of the search results are `stack-overflow`.
+
+```tsx
+ case 'stack-overflow':
+ return (
+
+ );
+```
+
+#### Use Stack Overflow Questions on your homepage
+
+Before you are able to add the stack overflow question component to your homepage, you need to go through the [homepage getting started guide](https://backstage.io/docs/getting-started/homepage). When its ready, add the following code snippet to your `packages/app/src/components/home/HomePage.tsx` file.
+
+```tsx
+
+
+
+```
diff --git a/plugins/stack-overflow/package.json b/plugins/stack-overflow/package.json
new file mode 100644
index 0000000000..350f4aa0ca
--- /dev/null
+++ b/plugins/stack-overflow/package.json
@@ -0,0 +1,56 @@
+{
+ "name": "@backstage/plugin-stack-overflow",
+ "version": "0.0.0",
+ "main": "src/index.ts",
+ "types": "src/index.ts",
+ "license": "Apache-2.0",
+ "publishConfig": {
+ "access": "public",
+ "main": "dist/index.esm.js",
+ "types": "dist/index.d.ts"
+ },
+ "scripts": {
+ "build": "backstage-cli plugin:build",
+ "start": "backstage-cli plugin:serve",
+ "lint": "backstage-cli lint",
+ "test": "backstage-cli test",
+ "diff": "backstage-cli plugin:diff",
+ "prepack": "backstage-cli prepack",
+ "postpack": "backstage-cli postpack",
+ "clean": "backstage-cli clean"
+ },
+ "dependencies": {
+ "@backstage/core-components": "^0.8.8",
+ "@backstage/plugin-home": "^0.4.15",
+ "@backstage/core-plugin-api": "^0.6.0",
+ "@backstage/search-common": "^0.2.2",
+ "@backstage/config": "^0.1.13",
+ "@backstage/theme": "^0.2.14",
+ "@material-ui/core": "^4.12.2",
+ "@material-ui/icons": "^4.9.1",
+ "@material-ui/lab": "4.0.0-alpha.57",
+ "qs": "^6.10.3",
+ "cross-fetch": "^3.0.6",
+ "lodash": "^4.17.21",
+ "react-use": "^17.2.4",
+ "@testing-library/jest-dom": "^5.10.1"
+ },
+ "peerDependencies": {
+ "react": "^16.13.1 || ^17.0.0"
+ },
+ "devDependencies": {
+ "@backstage/cli": "^0.13.2-next.0",
+ "@backstage/core-app-api": "^0.5.2",
+ "@backstage/dev-utils": "^0.2.21-next.0",
+ "@backstage/test-utils": "^0.2.4",
+ "@testing-library/react": "^11.2.5",
+ "@testing-library/user-event": "^13.1.8",
+ "@types/jest": "*",
+ "@types/node": "*",
+ "msw": "^0.35.0",
+ "cross-fetch": "^3.0.6"
+ },
+ "files": [
+ "dist"
+ ]
+}
diff --git a/plugins/stack-overflow/src/home/StackOverflowQuestions/Content.tsx b/plugins/stack-overflow/src/home/StackOverflowQuestions/Content.tsx
new file mode 100644
index 0000000000..a0832fc1f1
--- /dev/null
+++ b/plugins/stack-overflow/src/home/StackOverflowQuestions/Content.tsx
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useApi, configApiRef } from '@backstage/core-plugin-api';
+import { Link } from '@backstage/core-components';
+import {
+ IconButton,
+ List,
+ ListItem,
+ ListItemText,
+ ListItemSecondaryAction,
+} from '@material-ui/core';
+import OpenInNewIcon from '@material-ui/icons/OpenInNew';
+import { useAsync } from 'react-use';
+import qs from 'qs';
+import React from 'react';
+import {
+ StackOverflowQuestion,
+ StackOverflowQuestionsContentProps,
+} from '../../types';
+
+/**
+ * A component to display a list of stack overflow questions on the homepage.
+ *
+ * @public
+ */
+
+export const Content = (props: StackOverflowQuestionsContentProps) => {
+ const { requestParams } = props;
+ const configApi = useApi(configApiRef);
+ const baseUrl = configApi.getOptionalString('stackoverflow.baseUrl');
+
+ const { value, loading, error } = useAsync(async (): Promise<
+ StackOverflowQuestion[]
+ > => {
+ const params = requestParams ? `?${qs.stringify(requestParams)}` : '';
+ const response = await fetch(`${baseUrl}/questions${params}`);
+ const data = await response.json();
+ return data.items;
+ }, []);
+
+ if (loading) {
+ return
;
+ }
+
+ const getSecondaryText = (answer_count: Number) =>
+ answer_count > 1 ? `${answer_count} answers` : `${answer_count} answer`;
+
+ return (
+
+ {value.map(question => (
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ );
+};
diff --git a/plugins/stack-overflow/src/home/StackOverflowQuestions/StackOverflowQuestions.stories.tsx b/plugins/stack-overflow/src/home/StackOverflowQuestions/StackOverflowQuestions.stories.tsx
new file mode 100644
index 0000000000..e19e0f807b
--- /dev/null
+++ b/plugins/stack-overflow/src/home/StackOverflowQuestions/StackOverflowQuestions.stories.tsx
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { HomePageStackOverflowQuestions } from '../../plugin';
+import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils';
+import { configApiRef } from '@backstage/core-plugin-api';
+import { ConfigReader } from '@backstage/config';
+import { Grid } from '@material-ui/core';
+import React, { ComponentType } from 'react';
+
+export default {
+ title: 'Plugins/Home/Components/StackOverflow',
+ component: HomePageStackOverflowQuestions,
+ decorators: [
+ (Story: ComponentType<{}>) =>
+ wrapInTestApp(
+ <>
+
+
+
+ >,
+ ),
+ ],
+};
+
+export const Default = () => {
+ return (
+
+
+
+ );
+};
diff --git a/plugins/stack-overflow/src/home/StackOverflowQuestions/index.ts b/plugins/stack-overflow/src/home/StackOverflowQuestions/index.ts
new file mode 100644
index 0000000000..1faa9a2426
--- /dev/null
+++ b/plugins/stack-overflow/src/home/StackOverflowQuestions/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { Content } from './Content';
diff --git a/plugins/stack-overflow/src/index.ts b/plugins/stack-overflow/src/index.ts
new file mode 100644
index 0000000000..9e71988100
--- /dev/null
+++ b/plugins/stack-overflow/src/index.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export {
+ stackOverflowPlugin,
+ StackOverflowSearchResultListItem,
+ HomePageStackOverflowQuestions,
+} from './plugin';
+export { StackOverflowQuestionsCollator } from './search/StackOverflowQuestionsCollator';
diff --git a/plugins/stack-overflow/src/plugin.test.ts b/plugins/stack-overflow/src/plugin.test.ts
new file mode 100644
index 0000000000..b25cfe01ee
--- /dev/null
+++ b/plugins/stack-overflow/src/plugin.test.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { stackOverflowPlugin } from './plugin';
+
+describe('stack-overflow', () => {
+ it('should export plugin', () => {
+ expect(stackOverflowPlugin).toBeDefined();
+ });
+});
diff --git a/plugins/stack-overflow/src/plugin.ts b/plugins/stack-overflow/src/plugin.ts
new file mode 100644
index 0000000000..90a82da719
--- /dev/null
+++ b/plugins/stack-overflow/src/plugin.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+ createPlugin,
+ createComponentExtension,
+} from '@backstage/core-plugin-api';
+import { createCardExtension } from '@backstage/plugin-home';
+import { StackOverflowQuestionsContentProps } from './types';
+
+export const stackOverflowPlugin = createPlugin({
+ id: 'stack-overflow',
+});
+
+/**
+ * A component to display a stack overflow search result
+ *
+ * @public
+ */
+
+export const StackOverflowSearchResultListItem = stackOverflowPlugin.provide(
+ createComponentExtension({
+ name: 'StackOverflowResultListItem',
+ component: {
+ lazy: () =>
+ import('./search/StackOverflowSearchResultListItem').then(
+ m => m.StackOverflowSearchResultListItem,
+ ),
+ },
+ }),
+);
+
+/**
+ * A component to display a list of stack overflow questions on the homepage.
+ *
+ * @public
+ */
+export const HomePageStackOverflowQuestions = stackOverflowPlugin.provide(
+ createCardExtension({
+ name: 'HomePageStackOverflowQuestions',
+ title: 'Stack Overflow Questions',
+ components: () => import('./home/StackOverflowQuestions'),
+ }),
+);
diff --git a/plugins/stack-overflow/src/search/StackOverflowQuestionsCollator/StackOverflowQuestionsCollator.tsx b/plugins/stack-overflow/src/search/StackOverflowQuestionsCollator/StackOverflowQuestionsCollator.tsx
new file mode 100644
index 0000000000..a44b2f4742
--- /dev/null
+++ b/plugins/stack-overflow/src/search/StackOverflowQuestionsCollator/StackOverflowQuestionsCollator.tsx
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { IndexableDocument, DocumentCollator } from '@backstage/search-common';
+import { Config } from '@backstage/config';
+import fetch from 'cross-fetch';
+import qs from 'qs';
+import {
+ StackOverflowQuestion,
+ StackOverflowQuestionsRequestParams,
+} from '../../types';
+
+interface StackOverflowDocument extends IndexableDocument {
+ answers: number;
+ tags: string[];
+}
+
+export class StackOverflowQuestionsCollator implements DocumentCollator {
+ protected baseUrl: string;
+ protected requestParams: StackOverflowQuestionsRequestParams;
+ public readonly type: string = 'stack-overflow';
+
+ constructor({
+ config,
+ requestParams,
+ }: {
+ config: Config;
+ requestParams: StackOverflowQuestionsRequestParams;
+ }) {
+ this.baseUrl = config.getString('stackoverflow.baseUrl');
+ this.requestParams = requestParams;
+ }
+
+ async execute() {
+ const params = this.requestParams
+ ? `?${qs.stringify(this.requestParams)}`
+ : '';
+ const res = await fetch(`${this.baseUrl}/questions${params}`);
+
+ const data = await res.json();
+ return data.items.map(
+ ({
+ title,
+ link,
+ owner: { display_name },
+ tags,
+ answer_count,
+ }: StackOverflowQuestion): StackOverflowDocument => {
+ return {
+ title: title,
+ location: link,
+ text: display_name,
+ tags: tags,
+ answers: answer_count,
+ };
+ },
+ );
+ }
+}
diff --git a/plugins/stack-overflow/src/search/StackOverflowQuestionsCollator/index.ts b/plugins/stack-overflow/src/search/StackOverflowQuestionsCollator/index.ts
new file mode 100644
index 0000000000..d5fecd0dce
--- /dev/null
+++ b/plugins/stack-overflow/src/search/StackOverflowQuestionsCollator/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { StackOverflowQuestionsCollator } from './StackOverflowQuestionsCollator';
diff --git a/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.stories.tsx b/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.stories.tsx
new file mode 100644
index 0000000000..171a6203ef
--- /dev/null
+++ b/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.stories.tsx
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { StackOverflowSearchResultListItem } from '../../plugin';
+import { wrapInTestApp } from '@backstage/test-utils';
+import React, { ComponentType } from 'react';
+import OpenInNewIcon from '@material-ui/icons/OpenInNew';
+
+export default {
+ title: 'Plugins/Search/StackOverflowResultListItem',
+ component: StackOverflowSearchResultListItem,
+ decorators: [(Story: ComponentType<{}>) => wrapInTestApp()],
+};
+
+export const Default = () => {
+ return (
+
+ );
+};
diff --git a/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.test.tsx b/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.test.tsx
new file mode 100644
index 0000000000..61e901591e
--- /dev/null
+++ b/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.test.tsx
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { renderInTestApp } from '@backstage/test-utils';
+import { screen } from '@testing-library/react';
+import { StackOverflowSearchResultListItem } from './StackOverflowSearchResultListItem';
+
+describe('', () => {
+ it('should render without exploding', async () => {
+ await renderInTestApp(
+ ,
+ );
+ expect(
+ screen.getByText(/Customizing Spotify backstage UI/i),
+ ).toBeInTheDocument();
+ expect(screen.getByText(/Tag: backstage/i)).toBeInTheDocument();
+ expect(
+ screen.getByText(/Customizing Spotify backstage UI/i).closest('a'),
+ ).toHaveAttribute('href', 'https://stackoverflow.com/questions/7');
+ });
+});
diff --git a/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.tsx b/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.tsx
new file mode 100644
index 0000000000..52c16eeb77
--- /dev/null
+++ b/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.tsx
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import _unescape from 'lodash/unescape';
+import { Link } from '@backstage/core-components';
+import {
+ Divider,
+ ListItem,
+ ListItemText,
+ ListItemIcon,
+ Box,
+ Chip,
+} from '@material-ui/core';
+
+type StackOverflowSearchResultListItemProps = {
+ result: any; // TODO(emmaindal): type to StackOverflowDocument.
+ icon?: React.ReactNode;
+};
+
+export const StackOverflowSearchResultListItem = (
+ props: StackOverflowSearchResultListItemProps,
+) => {
+ const { location, title, text, answers, tags } = props.result;
+
+ return (
+
+
+ {props.icon && {props.icon}}
+
+
+
+ {tags &&
+ tags.map((tag: string) => (
+
+ ))}
+
+
+
+
+ );
+};
diff --git a/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/index.ts b/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/index.ts
new file mode 100644
index 0000000000..499f7273aa
--- /dev/null
+++ b/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { StackOverflowSearchResultListItem } from './StackOverflowSearchResultListItem';
diff --git a/plugins/stack-overflow/src/setupTests.ts b/plugins/stack-overflow/src/setupTests.ts
new file mode 100644
index 0000000000..9bb3e72355
--- /dev/null
+++ b/plugins/stack-overflow/src/setupTests.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import '@testing-library/jest-dom';
+import 'cross-fetch/polyfill';
diff --git a/plugins/stack-overflow/src/types.ts b/plugins/stack-overflow/src/types.ts
new file mode 100644
index 0000000000..a23585ed3c
--- /dev/null
+++ b/plugins/stack-overflow/src/types.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export type StackOverflowQuestion = {
+ title: string;
+ link: string;
+ owner: Record;
+ tags: string[];
+ answer_count: number;
+};
+
+export type StackOverflowQuestionsContentProps = {
+ requestParams: StackOverflowQuestionsRequestParams;
+};
+
+export type StackOverflowQuestionsRequestParams = {
+ [key: string]: string | string[] | number;
+};