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

feat: ADS Entity List Tree #38493

Open
wants to merge 59 commits into
base: release
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
ce99143
chore: Setup for Entity Item
hetunandu Jan 1, 2025
c28f6a2
chore: Editable State and stories
hetunandu Jan 1, 2025
962253f
chore: Fix list item changes
hetunandu Jan 2, 2025
019dccf
chore: Create new Editable submodule for shared usage
hetunandu Jan 2, 2025
f72e009
chore: Reuse ads editable in tabs
hetunandu Jan 2, 2025
98a50b4
Update tests
hetunandu Jan 2, 2025
b83ae34
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
ankitakinger Jan 2, 2025
8ba6531
Merge branch 'feat/ads/entity-item' of https://github.com/appsmithorg…
ankitakinger Jan 2, 2025
87ac4d8
fix: No permission to edit
hetunandu Jan 2, 2025
fd6c715
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
ankitakinger Jan 2, 2025
c03b0e1
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
ankitakinger Jan 2, 2025
cb6b0ad
updating spacings in list item component
ankitakinger Jan 2, 2025
2c3b12f
Merge branch 'feat/ads/entity-item' of https://github.com/appsmithorg…
ankitakinger Jan 2, 2025
e0c258a
minor change
ankitakinger Jan 2, 2025
9a9db48
css updates
ankitakinger Jan 2, 2025
c38364f
chore: First Draft
hetunandu Jan 3, 2025
58d5329
Merge branch 'feat/ads/entity-item' into feat/ads/entity-tree
hetunandu Jan 3, 2025
09a902e
chore: Fixed padding and styles
hetunandu Jan 3, 2025
0ac9545
chore: Add loading state
hetunandu Jan 3, 2025
4afc917
fix: missing dependency
hetunandu Jan 3, 2025
3679b9f
fix: Test case changes
hetunandu Jan 4, 2025
0bb665e
Merge branch 'release' into feat/ads/entity-item
hetunandu Jan 4, 2025
0978b1c
Merge branch 'release' into feat/ads/entity-item
hetunandu Jan 6, 2025
fa29de4
Merge branch 'feat/ads/entity-item' into feat/ads/entity-tree
hetunandu Jan 6, 2025
699c018
chore: ListItem hook and event improvements
hetunandu Jan 6, 2025
cfd157a
chore: Entity Tree improvements
hetunandu Jan 6, 2025
353e7c4
chore: Entity Tree story improvements
hetunandu Jan 6, 2025
a23a3c9
Merge branch 'release' into feat/ads/entity-tree
hetunandu Jan 6, 2025
74fe83e
chore: Update types and event handling
hetunandu Jan 6, 2025
d53a243
chore: Use feature flag to show new Entity Item tree
hetunandu Jan 6, 2025
fe2ab7b
chore: Update flag name
hetunandu Jan 6, 2025
ecd2ae3
fix: Update flag name
hetunandu Jan 6, 2025
7fab390
fix: jest test event mocking
hetunandu Jan 7, 2025
001e7a6
fix: Padding with depth is 0
hetunandu Jan 7, 2025
2b41c8a
chore: Rename and correct style application
hetunandu Jan 7, 2025
e161648
fix: Typo
hetunandu Jan 7, 2025
5f34be1
chore: Add more identifiers
hetunandu Jan 7, 2025
74ac8e0
chore: Move Widget Tree inside IDE / UI
hetunandu Jan 8, 2025
3b64e70
chore: Move Widget Tree inside IDE / UI
hetunandu Jan 8, 2025
40a1ae1
Merge remote-tracking branch 'origin/feat/ads/entity-tree' into feat/…
hetunandu Jan 8, 2025
3f68ddf
chore: Housekeeping of List Item and Entity Item
hetunandu Jan 8, 2025
5e37259
Merge branch 'chore/ads-listitem-updates' into feat/ads/entity-tree
hetunandu Jan 8, 2025
027fac7
chore: Some refactors
hetunandu Jan 8, 2025
c5e5b7b
Merge branch 'release' into feat/ads/entity-tree
hetunandu Jan 8, 2025
064ad76
refactor: Separate UIEntityListTree component logic into hooks and ut…
devin-ai-integration[bot] Jan 8, 2025
e835fc1
Merge branch 'release' into feat/ads/entity-tree
hetunandu Jan 8, 2025
a25c03e
Revert "refactor: Separate UIEntityListTree component logic into hook…
hetunandu Jan 9, 2025
38705a5
fix: Improve separations
hetunandu Jan 9, 2025
ccaf13e
fix: Null checks
hetunandu Jan 10, 2025
b744321
Merge branch 'release' into feat/ads/entity-tree
hetunandu Jan 10, 2025
9a8d80f
fix: implement design feedback
hetunandu Jan 10, 2025
034271e
revert type
hetunandu Jan 10, 2025
d566221
fix: Update paths to reduce cycles
hetunandu Jan 10, 2025
b971035
chore: Coderabbit suggestions
hetunandu Jan 10, 2025
5115bb4
add tests
hetunandu Jan 10, 2025
6bafb5e
fix: parent not expanded when children selected
hetunandu Jan 10, 2025
4f80a9a
chore: Padding left design update
hetunandu Jan 10, 2025
560ff97
chore: Test fix
hetunandu Jan 10, 2025
3bc67d9
Merge branch 'release' into feat/ads/entity-tree
hetunandu Jan 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React from "react";
import { Button, Flex, Icon, Text } from "../../..";
import { Button } from "../../../Button";
import { Flex } from "../../../Flex";
import { Icon } from "../../../Icon";
import { Text } from "../../../Text";
import type { EmptyStateProps } from "./EmptyState.types";

const EmptyState = ({ button, description, icon }: EmptyStateProps) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type IconNames, type ButtonKind } from "../../..";
import type { IconNames } from "../../../Icon";
import type { ButtonKind } from "../../../Button";

export interface EmptyStateProps {
icon: IconNames;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styled from "styled-components";
import { Text } from "../../..";
import { Text } from "../../../Text";

export const EntityEditableName = styled(Text)`
overflow: hidden;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useMemo } from "react";
import { ListItem, Spinner, Tooltip } from "../../..";
import { ListItem } from "../../../List";
import { Spinner } from "../../../Spinner";
import { Tooltip } from "../../../Tooltip";

import type { EntityItemProps } from "./EntityItem.types";
import { EntityEditableName } from "./EntityItem.styles";
Expand Down Expand Up @@ -32,11 +34,21 @@ export const EntityItem = (props: EntityItemProps) => {
onNameSave,
);

// When in loading state, start icon becomes the loading icon
const startIcon = useMemo(() => {
if (isLoading) {
return <Spinner size="md" />;
}

return props.startIcon;
}, [isLoading, props.startIcon]);

const inputProps = useMemo(
() => ({
onChange: handleTitleChange,
onKeyUp: handleKeyUp,
style: {
backgroundColor: "var(--ads-v2-color-bg)",
paddingTop: 0,
paddingBottom: 0,
height: "32px",
Expand All @@ -46,14 +58,7 @@ export const EntityItem = (props: EntityItemProps) => {
[handleKeyUp, handleTitleChange],
);

const startIcon = useMemo(() => {
if (isLoading) {
return <Spinner size="md" />;
}

return props.startIcon;
}, [isLoading, props.startIcon]);

// Use List Item custom title prop to show the editable name
const customTitle = useMemo(() => {
return (
<Tooltip
Expand All @@ -75,13 +80,23 @@ export const EntityItem = (props: EntityItemProps) => {
);
}, [editableName, inputProps, inputRef, inEditMode, validationError]);

// Do not show right control if the visibility is hover and the item is in edit mode
const rightControl = useMemo(() => {
if (props.rightControlVisibility === "hover" && inEditMode) {
return null;
}

return props.rightControl;
}, [inEditMode, props.rightControl, props.rightControlVisibility]);

return (
<ListItem
{...props}
className={clx("t--entity-item", props.className)}
customTitleComponent={customTitle}
data-testid={`t--entity-item-${props.title}`}
id={"entity-" + props.id}
rightControl={rightControl}
startIcon={startIcon}
/>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { EntityItem } from "./EntityItem";
export type { EntityItemProps } from "./EntityItem.types";
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/* eslint-disable no-console */
import React, { useEffect } from "react";
import type { Meta, StoryObj } from "@storybook/react";

import { EntityListTree } from "./EntityListTree";
import type {
EntityListTreeItem,
EntityListTreeProps,
} from "./EntityListTree.types";
import { ExplorerContainer } from "../ExplorerContainer";
import { Flex, Icon } from "../../..";
import { noop } from "lodash";

const meta: Meta<typeof EntityListTree> = {
title: "ADS/Templates/Entity Explorer/Entity List Tree",
component: EntityListTree,
};

export default meta;

const onClick = noop;
const nameEditorConfig = {
canEdit: true,
isEditing: false,
isLoading: false,
onEditComplete: noop,
onNameSave: noop,
validateName: () => null,
};

const Tree: EntityListTreeProps["items"] = [
{
startIcon: <Icon name="apps-line" />,
id: "1",
title: "Parent 1",
isExpanded: true,
onClick,
nameEditorConfig,
children: [
{
startIcon: <Icon name="apps-line" />,
id: "1.1",
title: "Child 1",
isExpanded: false,
isSelected: true,
onClick,
nameEditorConfig,
children: [
{
startIcon: <Icon name="apps-line" />,
id: "1.1.1",
title: "Grandchild 1",
isExpanded: false,
onClick,
nameEditorConfig,
},
{
startIcon: <Icon name="apps-line" />,
id: "1.1.2",
isDisabled: true,
title: "Grandchild 2",
isExpanded: false,
onClick,
nameEditorConfig,
},
],
},
{
startIcon: <Icon name="apps-line" />,
id: "1.2",
title: "Child 2",
isExpanded: false,
onClick,
nameEditorConfig,
},
],
},
{
startIcon: <Icon name="apps-line" />,
id: "2",
title: "Parent 2",
isExpanded: false,
onClick,
nameEditorConfig,
},
];

const treeUpdate = (
items: EntityListTreeProps["items"],
updater: (item: EntityListTreeItem) => EntityListTreeItem,
) => {
return items.map((item): EntityListTreeItem => {
return {
...updater(item),
children: item.children ? treeUpdate(item.children, updater) : undefined,
};
});
};

const Template = (props: { outsideSelection: string }) => {
const [expanded, setExpanded] = React.useState<Record<string, boolean>>({});
const [selected, setSelected] = React.useState<string | null>(
props.outsideSelection,
);
const [editing, setEditing] = React.useState<string | null>(null);

useEffect(
function handleSyncOfSelection() {
setSelected(props.outsideSelection);
},
[props.outsideSelection],
);

const onExpandClick = (id: string) => {
setExpanded((prev) => ({ ...prev, [id]: !Boolean(prev[id]) }));
};

const onItemSelect = (id: string) => {
setSelected(id);
};

const onItemEdit = (id: string) => {
setEditing(id);
};

const completeEdit = () => {
setEditing(null);
};

const updatedTree = treeUpdate(Tree, (item) => ({
...item,
isExpanded: Boolean(expanded[item.id]),
isSelected: item.id === selected,
onClick: () => onItemSelect(item.id),
onDoubleClick: () => onItemEdit(item.id),
nameEditorConfig: {
canEdit: true,
isEditing: item.id === editing,
isLoading: false,
onEditComplete: completeEdit,
onNameSave: noop,
validateName: () => null,
},
}));

return (
<Flex bg="white" overflow="hidden" width="400px">
<ExplorerContainer borderRight="STANDARD" height="500px" width="255px">
<Flex flexDirection="column" gap="spaces-2" p="spaces-3">
<EntityListTree items={updatedTree} onItemExpand={onExpandClick} />
</Flex>
</ExplorerContainer>
</Flex>
);
};

export const Basic = Template.bind({}) as StoryObj;

Basic.args = {
outsideSelection: "1",
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import styled from "styled-components";
import { Flex } from "../../../Flex";

/**
* This is used to add a spacing when collapse icon is not present
**/
export const CollapseSpacer = styled.div`
width: 17px;
`;

export const PaddingOverrider = styled.div`
width: 100%;

& > div {
/* Override the padding of the entity item since collapsible icon can be on the left
* By default the padding on the left is 8px, so we need to reduce it to 4px
**/
padding-left: 4px;
}
`;

export const CollapseWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
height: 16px;
width: 16px;
border-radius: var(--ads-v2-border-radius);
cursor: pointer;
`;

export const EntityItemWrapper = styled(Flex)<{ "data-depth": number }>`
border-radius: var(--ads-v2-border-radius);
cursor: pointer;

padding-left: ${(props) => {
return 4 + props["data-depth"] * 10;
}}px;

&[data-selected="true"] {
background-color: var(--ads-v2-colors-content-surface-active-bg);
}

/* disabled style */

&[data-disabled="true"] {
cursor: not-allowed;
opacity: var(--ads-v2-opacity-disabled);
background-color: var(--ads-v2-colors-content-surface-default-bg);
}

&:hover {
background-color: var(--ads-v2-colors-content-surface-hover-bg);
}

&:active {
background-color: var(--ads-v2-colors-content-surface-active-bg);
}

/* Focus styles */

&:focus-visible {
outline: var(--ads-v2-border-width-outline) solid
var(--ads-v2-color-outline);
outline-offset: var(--ads-v2-offset-outline);
}
`;
Loading
Loading