Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: implement new ProjectTree design with popout menus #4361

Merged
merged 31 commits into from
Oct 20, 2020

Conversation

beyackle
Copy link
Contributor

@beyackle beyackle commented Oct 13, 2020

Description

This swaps the Design View project tree to the new design, hopefully keeping all functionality intact while clearing space for the new form dialogs, added menu commands, and multi-skill bot handling.

Task Item

refs #3956

Screenshots

image

@beyackle beyackle marked this pull request as ready for review October 15, 2020 22:13
@srinaath srinaath self-assigned this Oct 16, 2020

// TODO: swap to the commented-out block once we're working on skills for real
// let { skillId } = props;
// if (skillId == null) skillId = projectId;
Copy link
Contributor

@srinaath srinaath Oct 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 const { location, dialogId, projectId = '' } = props;

So i dont see references to the skillId being set from the props. Essentially, with this current Design Page how would skills be supported. It seems like the schema, shellApi are all going to be only in the context of the rootBot. Should this be changed to

const { location, dialogId, projectId = '', skillId } = props;
const currentProjectId =  skillId || projectId

Now all the useRecoilValues would use something like useRecoilValue(schemasState(currentProjectId))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left skillId out of the props on purpose because, at this stage, we don't need it yet. This is a minimal change to get the new ProjectTree design in without overcomplicating things. The older branch I'm pulling stuff from did read skillId, just like you're suggesting.

@@ -173,16 +194,26 @@ const onRenderItem = (item: IOverflowSetItemProps) => {
/>
)}
{item.displayName}
{item.errorContent && (
Copy link
Contributor

@srinaath srinaath Oct 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these are the error indicators inside the project tree?

@zhixzhan This might be useful for the work your doing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@srinaath Sure, It helps.
One more requirements from my side is a missing style.
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. I won't make that change here, but if I could learn more about the specifics of this I'll make another PR for that. Are skills the only things that can be greyed out? How will the client know if a skill is broken/missing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets discuss this in the evenings status sync

groupRef.current?.toggleCollapseAll(true);
props.onToggleCollapse?.(props.group!);
onSelect(props.group!.key);
const botHasErrors = (bot: BotInProject) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: You could move it inside the selector. It would be cached once and recalculted only when dialogs change rather than being recalculated multiple times here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to keep it here, please use useMemo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about that. useMemo uses reference equality to check if it needs to update, and this is a live view of whether a bot or dialog has an error. If I type something into a trigger's actions that causes an error (like "=[", which doesn't parse), this produces error icons on the trigger and the dialog it belongs to. The work it's doing is only linear in the number of triggers as well, which I don't think is costly enough to warrant useMemo.

@@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation.
Copy link
Contributor

@srinaath srinaath Oct 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe move this file inside __mocks folder rather than directly inside testUtils

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea.

import { useState, MouseEvent, KeyboardEvent } from 'react';

type Props = {
children: JSX.Element;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: React.ReactNode instead of JSX

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@hatpick
Copy link
Contributor

hatpick commented Oct 19, 2020

Function declarations:
Sometimes arrow function style is used, and sometimes "function" keyword. Would you please stick to arrow functions?


type Props = {
children: JSX.Element;
summary: JSX.Element;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: React.ReactNode instead of JSX

`;

export const ExpandableNode = ({ children, summary, detailsRef, depth = 0 }: Props) => {
const [isOpen, setOpen] = useState(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: isExpanded

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see that it makes that much difference, but okay.

export const ExpandableNode = ({ children, summary, detailsRef, depth = 0 }: Props) => {
const [isOpen, setOpen] = useState(true);

function handleClick(ev: MouseEvent) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't the event type: React.MouseEvent<HTMLElement, MouseEvent>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not according to TypeScript. That's a standard HTML

tag, and following the chain of type declarations that VSCode gives me leads to this line in the TS lib:
onclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screen Shot 2020-10-19 at 2 41 53 PM

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the context of react, these are SyntheticEvent. However, as long as the type check is happy I'm fine.

@@ -201,6 +203,11 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
useEffect(() => {
if (location && props.dialogId && props.projectId) {
const { dialogId, projectId } = props;

// TODO: swap to the commented-out block once we're working on skills for real
Copy link
Contributor

@hatpick hatpick Oct 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a task id (github issue id) to finish this todo later on

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Will do.

@@ -455,7 +462,7 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
function handleBreadcrumbItemClick(_event, item) {
if (item) {
const { dialogId, selected, focused, index } = item;
selectAndFocus(projectId, dialogId, selected, focused, clearBreadcrumb(breadcrumb, index));
selectAndFocus(projectId, null, dialogId, selected, focused, clearBreadcrumb(breadcrumb, index));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it seems like this function args list is growing, how about changing it to accept an object so we know what is what by looking at it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea, but refactoring all of these should be its own PR later (which I don't being assigned to).

isRemote: boolean;
};

type IProjectTreeProps = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Interfaces usually start with I (capital i), types are just PascalCase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed (my usual convention is to just name this Props, because we already know what component we're looking at the props of - it's the file name).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure thing, if it's not exported Props will do just fine.


type IProjectTreeProps = {
onSelect?: (link: TreeLink) => void;
onAllSelected?: () => void;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: onSelectAll (to follow your other callbacks naming pattern)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I almost named it that, but that reads to me like there's some kind of "select-all" action - as in "select everything in the tree somehow" when what I really mean is that the literal link named "All" got selected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'll call this onSelectAllLink. That's different enough from "select all" that I think it reads better as "select the 'all' link".

groupRef.current?.toggleCollapseAll(true);
props.onToggleCollapse?.(props.group!);
onSelect(props.group!.key);
const botHasErrors = (bot: BotInProject) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to keep it here, please use useMemo

label: formatMessage('Remove this dialog'),
icon: 'Delete',
action: (link) => {
onDeleteDialog(link.dialogName ?? '');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does it mean to delete a dialog with blank name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's only there to make type-checking happy; this is inside the renderDialogHeader function, so we know that link.dialogName is never going to be undefined here, but TS doesn't necessarily know that.

},
},
]}
shiftOut={showTriggers ? 0 : 28}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Please keep the number in a variable with a descriptive name, otherwise it seems like a magic number

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Will fix this.

@cwhitten cwhitten merged commit 405f941 into main Oct 20, 2020
@cwhitten cwhitten deleted the beyackle/botProjectTreeOnly branch October 20, 2020 18:11
@cwhitten cwhitten mentioned this pull request Nov 13, 2020
lei9444 pushed a commit to lei9444/BotFramework-Composer-1 that referenced this pull request Jun 15, 2021
…t#4361)

* Update en-US.json

* bring in stuff from the draft branch

* make deletion work

* add error/warning icons

* read notification map for state

* fix type-checking and start on unit tests

* add sampleDialog and fix more tests

* add showAll

* rename to onAllSelected because it's a callback

* update unit tests

* fix onSelect handling in ProjectTree

* Update qna.test.tsx

* Update design.test.tsx

* add unit test

* fixes from PR comments

Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants