Refactor PluginInstaller component

Summary:
- Made side-effecting elements injectable via props.
- Added default props so usage doesn't change.
- Added testing-library/react snapshot test that waits for test data to appear in the list.

One slightly annoying part here is that this now that we have an `autoHeight` prop which is only useful for testing it as it prevents a problem with a height-detection in the test runner. We could even change the default as it doesn't affect the display in prod, but this still feels slightly cleaner.

Reviewed By: jknoxville

Differential Revision: D17808510

fbshipit-source-id: 2ae70886c58282d5bdc98ba4215e8248e4c7f159
This commit is contained in:
Pascal Hartig
2019-10-08 08:43:23 -07:00
committed by Facebook Github Bot
parent 3730c523ec
commit 04e12a28a0
3 changed files with 328 additions and 16 deletions

View File

@@ -41,7 +41,7 @@ const PluginManager = new PM({
ignoredDependencies: ['flipper', 'react', 'react-dom', '@types/*'],
});
type PluginDefinition = {
export type PluginDefinition = {
name: string;
version: string;
description: string;
@@ -92,10 +92,31 @@ const RestartBar = styled(FlexColumn)({
textAlign: 'center',
});
export default function() {
type Props = {
searchIndexFactory: () => algoliasearch.Index;
getInstalledPlugins: () => Promise<Map<string, PluginDefinition>>;
autoHeight: boolean;
};
const defaultProps: Props = {
searchIndexFactory: () => {
const client = algoliasearch(ALGOLIA_APPLICATION_ID, ALGOLIA_API_KEY);
return client.initIndex('npm-search');
},
getInstalledPlugins: _getInstalledPlugins,
autoHeight: false,
};
const PluginInstaller = function props(props: Props) {
const [restartRequired, setRestartRequired] = useState(false);
const [query, setQuery] = useState('');
const rows = useNPMSearch(setRestartRequired, query, setQuery);
const rows = useNPMSearch(
setRestartRequired,
query,
setQuery,
props.searchIndexFactory,
props.getInstalledPlugins,
);
const restartApp = useCallback(() => {
remote.app.relaunch();
remote.app.exit();
@@ -126,11 +147,14 @@ export default function() {
columns={columns}
highlightableRows={false}
highlightedRows={new Set()}
autoHeight={props.autoHeight}
rows={rows}
/>
</Container>
);
}
};
PluginInstaller.defaultProps = defaultProps;
export default PluginInstaller;
const TableButton = styled(Button)({
marginTop: 2,
@@ -248,29 +272,27 @@ function useNPMSearch(
setRestartRequired: (restart: boolean) => void,
query: string,
setQuery: (query: string) => void,
searchClientFactory: () => algoliasearch.Index,
getInstalledPlugins: () => Promise<Map<string, PluginDefinition>>,
): TableRows_immutable {
const index = useMemo(() => {
const client = algoliasearch(ALGOLIA_APPLICATION_ID, ALGOLIA_API_KEY);
return client.initIndex('npm-search');
}, []);
const index = useMemo(searchClientFactory, []);
const [installedPlugins, setInstalledPlugins] = useState(
new Map<string, PluginDefinition>(),
);
useEffect(() => {
reportUsage(`${TAG}:open`);
const getAndSetInstalledPlugins = () =>
reportPlatformFailures(
getInstalledPlugins(),
`${TAG}:getInstalledPlugins`,
).then(setInstalledPlugins);
useEffect(() => {
reportUsage(`${TAG}:open`);
getAndSetInstalledPlugins();
}, []);
const onInstall = useCallback(async () => {
reportPlatformFailures(
getInstalledPlugins(),
`${TAG}:getInstalledPlugins`,
).then(setInstalledPlugins);
getAndSetInstalledPlugins();
setRestartRequired(true);
}, []);
@@ -336,7 +358,7 @@ function useNPMSearch(
return List(results.map(createRow));
}
async function getInstalledPlugins() {
async function _getInstalledPlugins(): Promise<Map<string, PluginDefinition>> {
const dirs = await fs.readdir(PLUGIN_DIR);
const plugins = await Promise.all<[string, PluginDefinition]>(
dirs.map(