Skip to content

Commit 0506123

Browse files
authored
feat(gatsby-admin): plugin search (#25903)
* Copy plugin search from www into admin * Move to Combobox search * Cleanup * TypeScript cleanup * add algolia types * Fix syntax
1 parent a0c70d0 commit 0506123

File tree

5 files changed

+192
-84
lines changed

5 files changed

+192
-84
lines changed

packages/gatsby-admin/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,22 @@
1515
"@emotion/styled": "^10.0.27",
1616
"@typescript-eslint/eslint-plugin": "^2.28.0",
1717
"@typescript-eslint/parser": "^2.28.0",
18+
"@types/react-instantsearch-dom": "^5.2.6",
1819
"csstype": "^2.6.10",
1920
"formik": "^2.1.4",
2021
"gatsby": "^2.24.7",
2122
"gatsby-interface": "0.0.173",
2223
"gatsby-plugin-typescript": "^2.4.16",
2324
"gatsby-source-graphql": "^2.6.2",
25+
"lodash-es": "^4.17.15",
2426
"ncp": "^2.0.0",
2527
"nodemon": "^2.0.4",
2628
"react": "^16.12.0",
2729
"react-dom": "^16.12.0",
2830
"react-helmet": "^6.1.0",
2931
"react-icons": "^3.10.0",
32+
"react-instantsearch-dom": "^5.7.0",
33+
"remove-markdown": "^0.3.0",
3034
"strict-ui": "^0.1.3",
3135
"subscriptions-transport-ws": "^0.9.16",
3236
"theme-ui": "^0.4.0-alpha.3",
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/* @jsx jsx */
2+
import { jsx } from "strict-ui"
3+
import { Spinner } from "theme-ui"
4+
import {
5+
InstantSearch,
6+
Configure,
7+
RefinementList,
8+
ToggleRefinement,
9+
connectAutoComplete,
10+
} from "react-instantsearch-dom"
11+
import {
12+
Combobox,
13+
ComboboxInput,
14+
ComboboxPopover,
15+
ComboboxList,
16+
ComboboxOption,
17+
} from "gatsby-interface"
18+
import { useMutation } from "urql"
19+
20+
const SearchCombobox: React.FC<{
21+
onSelect: (value: string) => void
22+
}> = connectAutoComplete(({ hits, currentRefinement, refine, onSelect }) => (
23+
<Combobox onSelect={onSelect}>
24+
<ComboboxInput
25+
sx={{ width: `20em` }}
26+
aria-labelledby="plugin-search-label"
27+
onChange={(e): void => refine(e.target.value)}
28+
value={currentRefinement}
29+
/>
30+
<ComboboxPopover>
31+
<ComboboxList aria-labelledby="plugin-search-label">
32+
{hits.map(hit => (
33+
<ComboboxOption
34+
key={hit.objectID}
35+
selected={false}
36+
value={hit.name}
37+
></ComboboxOption>
38+
))}
39+
</ComboboxList>
40+
</ComboboxPopover>
41+
</Combobox>
42+
))
43+
44+
// the search bar holds the Search component in the InstantSearch widget
45+
const PluginSearchInput: React.FC<{}> = () => {
46+
const [{ fetching }, installGatbyPlugin] = useMutation(`
47+
mutation installGatsbyPlugin($name: String!) {
48+
createNpmPackage(npmPackage: {
49+
name: $name,
50+
dependencyType: "production"
51+
}) {
52+
id
53+
name
54+
}
55+
createGatsbyPlugin(gatsbyPlugin: {
56+
name: $name
57+
}) {
58+
id
59+
name
60+
}
61+
}
62+
`)
63+
64+
return (
65+
<div>
66+
<InstantSearch
67+
apiKey="ae43b69014c017e05950a1cd4273f404"
68+
appId="OFCNCOG2CU"
69+
indexName="npm-search"
70+
>
71+
<div style={{ display: `none` }}>
72+
<Configure analyticsTags={[`gatsby-plugins`]} />
73+
<RefinementList
74+
attribute="keywords"
75+
transformItems={(items: Array<any>): Array<any> =>
76+
items.map(({ count, ...item }) => {
77+
return {
78+
...item,
79+
count: count || 0,
80+
}
81+
})
82+
}
83+
defaultRefinement={[`gatsby-component`, `gatsby-plugin`]}
84+
/>
85+
<ToggleRefinement
86+
attribute="deprecated"
87+
value={false}
88+
label="No deprecated plugins"
89+
defaultRefinement={true}
90+
/>
91+
</div>
92+
{fetching ? (
93+
<Spinner />
94+
) : (
95+
<SearchCombobox
96+
onSelect={(value): void => {
97+
installGatbyPlugin({ name: value })
98+
}}
99+
/>
100+
)}
101+
</InstantSearch>
102+
</div>
103+
)
104+
}
105+
106+
export default PluginSearchInput

packages/gatsby-admin/src/components/providers.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const theme = {
2828
"100%": `100%`,
2929
"16px": `16px`,
3030
"15em": `15em`,
31+
"20em": `20em`,
3132
initial: `initial`,
3233
},
3334
}

packages/gatsby-admin/src/pages/index.tsx

Lines changed: 7 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,18 @@
11
/** @jsx jsx */
22
import React from "react"
33
import { jsx, Flex, Grid } from "strict-ui"
4+
import { Spinner } from "theme-ui"
45
import { useQuery, useMutation } from "urql"
56
import {
67
Heading,
78
HeadingProps,
89
Text,
9-
Button,
10-
InputField,
11-
InputFieldControl,
12-
ButtonProps,
13-
InputFieldLabel,
1410
DropdownMenu,
1511
DropdownMenuButton,
1612
DropdownMenuItem,
1713
DropdownMenuItems,
1814
} from "gatsby-interface"
19-
20-
const SecondaryButton: React.FC<ButtonProps> = props => (
21-
<Button
22-
variant="SECONDARY"
23-
size="S"
24-
sx={{
25-
paddingX: 6,
26-
paddingY: 4,
27-
}}
28-
{...props}
29-
></Button>
30-
)
31-
32-
const InstallInput: React.FC<{ for: string }> = props => {
33-
const inputId = `install-${props.for}`
34-
const [value, setValue] = React.useState(``)
35-
36-
const [{ fetching }, installGatbyPlugin] = useMutation(`
37-
mutation installGatsbyPlugin($name: String!) {
38-
createNpmPackage(npmPackage: {
39-
name: $name,
40-
dependencyType: "production"
41-
}) {
42-
id
43-
name
44-
}
45-
createGatsbyPlugin(gatsbyPlugin: {
46-
name: $name
47-
}) {
48-
id
49-
name
50-
}
51-
}
52-
`)
53-
54-
return (
55-
<form
56-
onSubmit={(evt): void => {
57-
evt.preventDefault()
58-
if (value.indexOf(`gatsby-`) !== 0) return
59-
60-
installGatbyPlugin({
61-
name: value,
62-
})
63-
}}
64-
>
65-
<InputField id={inputId}>
66-
<Flex gap={2} flexDirection="column">
67-
<InputFieldLabel>Install {props.for}:</InputFieldLabel>
68-
<Flex gap={4} alignItems="center">
69-
<InputFieldControl
70-
placeholder={`gatsby-${props.for}-`}
71-
disabled={fetching}
72-
value={value}
73-
onChange={(e): void => setValue(e.target.value)}
74-
sx={{
75-
width: `initial`,
76-
}}
77-
/>
78-
<SecondaryButton
79-
disabled={!value.trim()}
80-
loading={fetching}
81-
loadingLabel="Installing"
82-
>
83-
Install
84-
</SecondaryButton>
85-
</Flex>
86-
</Flex>
87-
</InputField>
88-
</form>
89-
)
90-
}
15+
import PluginSearchBar from "../components/plugin-search"
9116

9217
const SectionHeading: React.FC<HeadingProps> = props => (
9318
<Heading as="h1" sx={{ fontWeight: `500`, fontSize: 5 }} {...props} />
@@ -181,7 +106,7 @@ const Index: React.FC<{}> = () => {
181106
`,
182107
})
183108

184-
if (fetching) return <p>Loading...</p>
109+
if (fetching) return <Spinner />
185110

186111
if (error) return <p>Oops something went wrong.</p>
187112

@@ -204,13 +129,15 @@ const Index: React.FC<{}> = () => {
204129
))}
205130
</ul>
206131

207-
<SectionHeading>Installed Plugins</SectionHeading>
132+
<SectionHeading id="plugin-search-label">
133+
Installed Plugins
134+
</SectionHeading>
208135
<Grid gap={6} columns={[1, 1, 1, 2, 3]}>
209136
{data.allGatsbyPlugin.nodes.map(plugin => (
210137
<PluginCard key={plugin.id} plugin={plugin} />
211138
))}
212139
</Grid>
213-
<InstallInput for="plugin" />
140+
<PluginSearchBar />
214141
</Flex>
215142
)
216143
}

0 commit comments

Comments
 (0)