Skip to content

Commit

Permalink
feat: apply new deisgn on project tree to solve Critical create actio…
Browse files Browse the repository at this point in the history
…ns inaccessible issue on Project Tree (#5722)

* save for tmp use

* save tmp code

* save for tmp use

* add two more buttons

* update calculation for treeitem text maxwidth

* show parent icons if a link selected

* fix resize bug

* fix resize bug

* remove unused file

* handle comments

* fix truncated treeitem width bug

* remove addTrigger and addDialog Icons

Co-authored-by: Dong Lei <donglei@microsoft.com>
  • Loading branch information
liweitian and boydc2014 authored Feb 10, 2021
1 parent 10523f6 commit ff1b86a
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/** @jsx jsx */
import { jsx, css } from '@emotion/core';
import { useState, MouseEvent, KeyboardEvent } from 'react';
import { NeutralColors } from '@uifabric/fluent-theme';

import { INDENT_PER_LEVEL } from './constants';

Expand All @@ -14,18 +15,22 @@ type Props = {
detailsRef?: (el: HTMLElement | null) => void;
onToggle?: (newState: boolean) => void;
defaultState?: boolean;
isActive?: boolean;
};

const summaryStyle = css`
const summaryStyle = (depth: number, isActive: boolean) => css`
label: summary;
display: flex;
padding-left: 12px;
padding-left: ${depth * INDENT_PER_LEVEL + 12}px;
padding-top: 6px;
:hover {
background: ${isActive ? NeutralColors.gray40 : NeutralColors.gray20};
}
background: ${isActive ? NeutralColors.gray30 : NeutralColors.white};
`;

const nodeStyle = (depth: number) => css`
const nodeStyle = css`
margin-top: 2px;
margin-left: ${depth * INDENT_PER_LEVEL}px;
`;

const TRIANGLE_SCALE = 0.6;
Expand All @@ -42,7 +47,15 @@ const detailsStyle = css`
}
`;

export const ExpandableNode = ({ children, summary, detailsRef, depth = 0, onToggle, defaultState = true }: Props) => {
export const ExpandableNode = ({
children,
summary,
detailsRef,
depth = 0,
onToggle,
defaultState = true,
isActive = false,
}: Props) => {
const [isExpanded, setExpanded] = useState(defaultState);

function setExpandedWithCallback(newState: boolean) {
Expand All @@ -62,11 +75,11 @@ export const ExpandableNode = ({ children, summary, detailsRef, depth = 0, onTog
}

return (
<div css={nodeStyle(depth)} data-testid="dialog">
<div css={nodeStyle} data-testid="dialog">
<details ref={detailsRef} css={detailsStyle} open={isExpanded}>
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/no-noninteractive-tabindex */}
<summary
css={summaryStyle}
css={summaryStyle(depth, isActive)}
data-testid={'summaryTag'}
role="button"
tabIndex={0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import TelemetryClient from '../../telemetry/TelemetryClient';
import { createBotSettingUrl, navigateTo } from '../../utils/navigation';

import { TreeLink, ProjectTreeOptions } from './ProjectTree';
import { doesLinkMatch } from './helpers';
import { isChildDialogLinkSelected, doesLinkMatch } from './helpers';
import { TreeItem } from './treeItem';

const icons = {
Expand Down Expand Up @@ -161,6 +161,7 @@ export const ProjectHeader = (props: ProjectHeaderProps) => {
hasChildren={!isRemote}
icon={isRemote ? icons.EXTERNAL_SKILL : icons.BOT}
isActive={doesLinkMatch(link, selectedLink)}
isChildSelected={isChildDialogLinkSelected(link, selectedLink)}
isMenuOpen={isMenuOpen}
link={link}
menu={options.showMenu ? menu : []}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

/** @jsx jsx */
import React, { useCallback, useState, useRef } from 'react';
import { NeutralColors } from '@uifabric/fluent-theme';
import { jsx, css } from '@emotion/core';
import { SearchBox } from 'office-ui-fabric-react/lib/SearchBox';
import { FocusZone, FocusZoneDirection } from 'office-ui-fabric-react/lib/FocusZone';
Expand Down Expand Up @@ -32,7 +33,7 @@ import { TreeItem } from './treeItem';
import { ExpandableNode } from './ExpandableNode';
import { INDENT_PER_LEVEL } from './constants';
import { ProjectTreeHeader, ProjectTreeHeaderMenuItem } from './ProjectTreeHeader';
import { doesLinkMatch } from './helpers';
import { isChildTriggerLinkSelected, doesLinkMatch } from './helpers';
import { ProjectHeader } from './ProjectHeader';

// -------------------- Styles -------------------- //
Expand Down Expand Up @@ -71,10 +72,14 @@ const tree = css`
label: tree;
`;

const headerCSS = (label: string) => css`
const headerCSS = (label: string, isActive?: boolean) => css`
margin-top: -6px;
width: 100%;
label: ${label};
:hover {
background: ${isActive ? NeutralColors.gray40 : NeutralColors.gray20};
}
background: ${isActive ? NeutralColors.gray30 : NeutralColors.white};
`;

// -------------------- Helper functions -------------------- //
Expand Down Expand Up @@ -225,7 +230,7 @@ export const ProjectTree: React.FC<Props> = ({
// TODO Refactor to make sure tree is not generated until a new trigger/dialog is added. #5462
const createSubtree = useCallback(() => {
return projectCollection.map(createBotSubtree);
}, [projectCollection, selectedLink]);
}, [projectCollection, selectedLink, leftSplitWidth]);

if (rootProjectId == null) {
// this should only happen before a project is loaded in, so it won't last very long
Expand Down Expand Up @@ -315,14 +320,15 @@ export const ProjectTree: React.FC<Props> = ({
<span
key={dialog.id}
ref={dialog.isRoot ? addMainDialogRef : null}
css={headerCSS('dialog-header')}
css={headerCSS('dialog-header', doesLinkMatch(dialogLink, selectedLink))}
data-testid={`DialogHeader-${dialog.displayName}`}
role="grid"
>
<TreeItem
hasChildren
icon={isFormDialog ? icons.FORM_DIALOG : icons.DIALOG}
isActive={doesLinkMatch(dialogLink, selectedLink)}
isChildSelected={isChildTriggerLinkSelected(dialogLink, selectedLink)}
isMenuOpen={isMenuOpen}
link={dialogLink}
menu={options.showMenu ? menu : options.showQnAMenu ? [QnAMenuItem] : []}
Expand Down Expand Up @@ -376,7 +382,8 @@ export const ProjectTree: React.FC<Props> = ({
},
dialog: DialogInfo,
projectId: string,
dialogLink: TreeLink
dialogLink: TreeLink,
depth: number
): React.ReactNode => {
const link: TreeLink = {
projectId: rootProjectId,
Expand All @@ -399,6 +406,7 @@ export const ProjectTree: React.FC<Props> = ({
isActive={doesLinkMatch(link, selectedLink)}
isMenuOpen={isMenuOpen}
link={link}
marginLeft={depth * INDENT_PER_LEVEL}
menu={
options.showDelete
? [
Expand Down Expand Up @@ -430,7 +438,13 @@ export const ProjectTree: React.FC<Props> = ({
return scope.toLowerCase().includes(filter.toLowerCase());
};

const renderTriggerList = (triggers: ITrigger[], dialog: DialogInfo, projectId: string, dialogLink: TreeLink) => {
const renderTriggerList = (
triggers: ITrigger[],
dialog: DialogInfo,
projectId: string,
dialogLink: TreeLink,
depth: number
) => {
return triggers
.filter((tr) => filterMatch(dialog.displayName) || filterMatch(getTriggerName(tr)))
.map((tr) => {
Expand All @@ -443,7 +457,8 @@ export const ProjectTree: React.FC<Props> = ({
{ ...tr, index, displayName: getTriggerName(tr), warningContent, errorContent },
dialog,
projectId,
dialogLink
dialogLink,
depth
);
});
};
Expand Down Expand Up @@ -499,7 +514,7 @@ export const ProjectTree: React.FC<Props> = ({
summary={renderTriggerGroupHeader(groupDisplayName, dialog, projectId)}
onToggle={(newState) => setPageElement(key, newState)}
>
<div>{renderTriggerList(triggers, dialog, projectId, link)}</div>
<div>{renderTriggerList(triggers, dialog, projectId, link, 1)}</div>
</ExpandableNode>
);
};
Expand All @@ -520,7 +535,7 @@ export const ProjectTree: React.FC<Props> = ({
const renderDialogTriggers = (dialog: DialogInfo, projectId: string, startDepth: number, dialogLink: TreeLink) => {
return dialogIsFormDialog(dialog)
? renderDialogTriggersByProperty(dialog, projectId, startDepth + 1)
: renderTriggerList(dialog.triggers, dialog, projectId, dialogLink);
: renderTriggerList(dialog.triggers, dialog, projectId, dialogLink, 1);
};

const renderLgImport = (
Expand Down Expand Up @@ -650,6 +665,7 @@ export const ProjectTree: React.FC<Props> = ({
defaultState={getPageElement(key)}
depth={startDepth}
detailsRef={dialog.isRoot ? addMainDialogRef : undefined}
isActive={doesLinkMatch(dialogLink, selectedLink)}
summary={summaryElement}
onToggle={(newState) => setPageElement(key, newState)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@

export const SUMMARY_ARROW_SPACE = 28; // the rough pixel size of the dropdown arrow to the left of a Details/Summary element
export const INDENT_PER_LEVEL = 16;
export const ACTION_ICON_WIDTH = 28;
export const THREE_DOTS_ICON_WIDTH = 28;
10 changes: 10 additions & 0 deletions Composer/packages/client/src/components/ProjectTree/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ export const doesLinkMatch = (linkInTree?: Partial<TreeLink>, selectedLink?: Par
linkInTree.luFileId === selectedLink.luFileId
);
};

export const isChildTriggerLinkSelected = (linkInTree?: Partial<TreeLink>, selectedLink?: Partial<TreeLink>) => {
if (linkInTree == null || selectedLink == null) return false;
return linkInTree.skillId === selectedLink.skillId && linkInTree.dialogId === selectedLink.dialogId;
};

export const isChildDialogLinkSelected = (linkInTree?: Partial<TreeLink>, selectedLink?: Partial<TreeLink>) => {
if (linkInTree == null || selectedLink == null) return false;
return linkInTree.skillId === selectedLink.skillId;
};
Loading

0 comments on commit ff1b86a

Please sign in to comment.