licenses/LicenseDialog: use Tree instead of ButtonGroup

This commit is contained in:
David Lechner
2022-03-15 20:13:49 -05:00
parent 57cd40d9dd
commit 644ac4cb5e
2 changed files with 39 additions and 19 deletions
+6 -3
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { Classes } from '@blueprintjs/core';
import { cleanup } from '@testing-library/react';
import React from 'react';
import { testRender } from '../../test';
@@ -41,13 +42,15 @@ describe('LicenseDialog', () => {
);
// have to wait for async fetch
const button = await dialog.findByText('super-duper', { selector: 'button *' });
const treeNode = await dialog.findByText('super-duper', {
selector: `.${Classes.TREE_NODE} *`,
});
// when the dialog is first show, no license is selected
expect(dialog.queryByText('Joe Somebody')).toBeNull();
// then when you click on a license button, the license is shown
button.click();
// then when you click on a license name, the license is shown
treeNode.click();
expect(dialog.getByText('Joe Somebody')).toBeDefined();
});
});
+33 -16
View File
@@ -4,17 +4,18 @@
// The license dialog
import {
Button,
ButtonGroup,
Callout,
Card,
Classes,
Dialog,
NonIdealState,
Spinner,
Tree,
TreeEventHandler,
TreeNodeInfo,
} from '@blueprintjs/core';
import { useI18n } from '@shopify/react-i18n';
import React, { useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { useFetch } from 'usehooks-ts';
import { appName } from '../app/constants';
import { LicenseStringId } from './i18n';
@@ -33,7 +34,7 @@ interface LicenseInfo {
type LicenseList = ReadonlyArray<LicenseInfo>;
type LicenseListPanelProps = {
onItemClick(info: LicenseInfo): void;
onItemClick(info?: LicenseInfo): void;
};
const LicenseListPanel: React.VoidFunctionComponent<LicenseListPanelProps> = ({
@@ -41,10 +42,32 @@ const LicenseListPanel: React.VoidFunctionComponent<LicenseListPanelProps> = ({
}) => {
const [i18n] = useI18n({ id: 'license', translations: { en }, fallback: en });
const { data, error } = useFetch<LicenseList>('static/oss-licenses.json');
const [selectedNode, setSelectedNode] = useState<string | undefined>(undefined);
const contents = useMemo(() => {
if (!data) {
return undefined;
}
return data.map<TreeNodeInfo<LicenseInfo>>((info, i) => ({
id: i,
label: info.name,
isSelected: info.name === selectedNode,
nodeData: info,
}));
}, [data, selectedNode]);
const handleNodeClick = useCallback<TreeEventHandler<LicenseInfo>>(
(e) => {
setSelectedNode(e.nodeData?.name);
onItemClick(e.nodeData);
},
[setSelectedNode, onItemClick],
);
return (
<div className="pb-license-list">
{!data ? (
{contents === undefined ? (
<NonIdealState>
{error ? (
i18n.translate(LicenseStringId.ErrorFetchFailed)
@@ -53,21 +76,15 @@ const LicenseListPanel: React.VoidFunctionComponent<LicenseListPanelProps> = ({
)}
</NonIdealState>
) : (
<ButtonGroup minimal={true} vertical={true} alignText="left">
{data.map((info, i) => (
<Button key={i} onClick={() => onItemClick(info)}>
{info.name}
</Button>
))}
</ButtonGroup>
<Tree contents={contents} onNodeClick={handleNodeClick} />
)}
</div>
);
};
type LicenseInfoPanelProps = {
/** The license info to show or null if no license info is selected. */
licenseInfo: LicenseInfo | null;
/** The license info to show or undefined if no license info is selected. */
licenseInfo: LicenseInfo | undefined;
};
const LicenseInfoPanel = React.forwardRef<HTMLDivElement, LicenseInfoPanelProps>(
@@ -76,7 +93,7 @@ const LicenseInfoPanel = React.forwardRef<HTMLDivElement, LicenseInfoPanelProps>
return (
<div className="pb-license-info" ref={ref}>
{licenseInfo == null ? (
{licenseInfo === undefined ? (
<NonIdealState>
{i18n.translate(LicenseStringId.SelectPackageHelp)}
</NonIdealState>
@@ -128,7 +145,7 @@ const LicenseDialog: React.VoidFunctionComponent<LicenseDialogProps> = ({
isOpen,
onClose,
}) => {
const [licenseInfo, setLicenseInfo] = useState<LicenseInfo | null>(null);
const [licenseInfo, setLicenseInfo] = useState<LicenseInfo | undefined>(undefined);
const infoDiv = React.useRef<HTMLDivElement>(null);
const [i18n] = useI18n({ id: 'license', translations: { en }, fallback: en });