From 3d97706da3944a657fb05cd91e91cdd9e96aab14 Mon Sep 17 00:00:00 2001 From: Ankita Kinger Date: Fri, 10 Jan 2025 18:34:05 +0530 Subject: [PATCH] chore: Adding Group component in ADS Templates (#38512) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Adding Group component in ADS Templates to use the same for a single list of grouped entities in the product. Fixes [#37615](https://github.com/appsmithorg/appsmith/issues/37615) [#37616](https://github.com/appsmithorg/appsmith/issues/37616) [#38288](https://github.com/appsmithorg/appsmith/issues/38288) [#38287](https://github.com/appsmithorg/appsmith/issues/38287) ## Automation /ok-to-test tags="@tag.All" ### :mag: Cypress test results > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: > Commit: b62fecb2aeb4f3ebc9a9ed683f5d1d1949a943cc > Cypress dashboard. > Tags: `@tag.All` > Spec: >
Fri, 10 Jan 2025 09:11:50 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No ## Summary by CodeRabbit ## Release Notes - **New Features** - Enhanced List component with improved grouping capabilities. - Added support for dynamic list rendering with custom children. - Introduced `EntityGroupsList` for more flexible group management. - Added new styled components for better visual representation of lists. - Added `LoadMore` styled component for improved UX in entity groups. - New Storybook examples for `EntityGroupsList` and `EntityGroup` components. - **Improvements** - Refined List component to support more flexible item rendering. - Updated styling for list components. - Improved type safety for list-related components. - Streamlined code structure for better maintainability. - **Changes** - Removed `GroupedList` component. - Updated list rendering across multiple components. - Simplified list item management. - Expanded public API for `EntityExplorer` module with new exports. --- .../ads/src/List/List.stories.tsx | 68 ++++--- .../ads/src/List/List.styles.tsx | 22 ++- .../design-system/ads/src/List/List.tsx | 16 +- .../design-system/ads/src/List/List.types.tsx | 3 +- .../EntityGroupsList.stories.tsx | 185 ++++++++++++++++++ .../EntityGroupsList.styles.ts | 8 + .../EntityGroupsList/EntityGroupsList.tsx | 80 ++++++++ .../EntityGroupsList.types.ts | 19 ++ .../EntityExplorer/EntityGroupsList/index.ts | 5 + .../EntityExplorer/EntityItem/index.ts | 1 + .../ads/src/Templates/EntityExplorer/index.ts | 1 + .../Templates/IDEHeader/IDEHeader.stories.tsx | 29 +-- .../Editor/IDE/EditorPane/Query/hooks.tsx | 40 +++- .../StateInspector/StateInspector.tsx | 7 +- .../Preview/Debugger/helpDropdown.tsx | 78 ++++---- .../pages/Editor/IDE/EditorPane/JS/Add.tsx | 12 +- .../pages/Editor/IDE/EditorPane/Query/Add.tsx | 23 +-- .../IDE/EditorPane/components/Group.tsx | 79 -------- .../IDE/EditorPane/components/GroupedList.tsx | 35 ---- .../IDE/EditorPane/components/constants.ts | 1 - .../Editor/IDE/LeftPane/DataSidePane.tsx | 39 ++-- .../components/CreateNewQueryModal.tsx | 25 +-- 22 files changed, 518 insertions(+), 258 deletions(-) create mode 100644 app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.stories.tsx create mode 100644 app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.styles.ts create mode 100644 app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.tsx create mode 100644 app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.types.ts create mode 100644 app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/index.ts delete mode 100644 app/client/src/pages/Editor/IDE/EditorPane/components/Group.tsx delete mode 100644 app/client/src/pages/Editor/IDE/EditorPane/components/GroupedList.tsx delete mode 100644 app/client/src/pages/Editor/IDE/EditorPane/components/constants.ts diff --git a/app/client/packages/design-system/ads/src/List/List.stories.tsx b/app/client/packages/design-system/ads/src/List/List.stories.tsx index 2b86c011a0..fbeb7039c0 100644 --- a/app/client/packages/design-system/ads/src/List/List.stories.tsx +++ b/app/client/packages/design-system/ads/src/List/List.stories.tsx @@ -26,39 +26,47 @@ const ListTemplate = (args: ListProps) => { return ; }; +const items = [ + { + startIcon: , + title: "Action item 1", + }, + { + startIcon: , + title: "Action item 2", + }, + { + startIcon: , + title: "Action item 3", + }, + { + startIcon: , + title: "Action item 4", + }, + { + startIcon: , + title: "Action item 5", + }, + { + startIcon: , + title: "Action item 6", + }, + { + startIcon: , + title: "Action item 7", + }, +]; + export const ListStory = ListTemplate.bind({}) as StoryObj; ListStory.storyName = "List"; ListStory.args = { - items: [ - { - startIcon: , - title: "Action item 1", - }, - { - startIcon: , - title: "Action item 2", - }, - { - startIcon: , - title: "Action item 3", - }, - { - startIcon: , - title: "Action item 4", - }, - { - startIcon: , - title: "Action item 5", - }, - { - startIcon: , - title: "Action item 6", - }, - { - startIcon: , - title: "Action item 7", - }, - ], + children: items.map((item) => ( + alert("Clicked")} + /> + )), }; const ListItemArgTypes = { diff --git a/app/client/packages/design-system/ads/src/List/List.styles.tsx b/app/client/packages/design-system/ads/src/List/List.styles.tsx index adb2867559..15efd1bf33 100644 --- a/app/client/packages/design-system/ads/src/List/List.styles.tsx +++ b/app/client/packages/design-system/ads/src/List/List.styles.tsx @@ -6,6 +6,8 @@ import { ListItemTextOverflowClassName, ListItemTitleClassName, } from "./List.constants"; +import { Flex } from "../Flex"; +import { Text } from "../Text"; const Variables = css` --listitem-title-font-size: var(--ads-v2-font-size-4); @@ -71,7 +73,6 @@ export const StyledList = styled.div` padding: var(--ads-v2-spaces-1); display: flex; flex-direction: column; - gap: var(--ads-v2-spaces-2); `; export const StyledListItem = styled.div<{ @@ -159,3 +160,22 @@ export const StyledListItem = styled.div<{ padding-right: var(--ads-v2-spaces-2); } `; + +export const StyledGroup = styled(Flex)` + & .ads-v2-listitem .ads-v2-listitem__idesc { + opacity: 0; + } + + & .ads-v2-listitem:hover .ads-v2-listitem__idesc { + opacity: 1; + } +`; + +export const GroupedList = styled(StyledList)` + padding: 0; +`; + +export const GroupTitle = styled(Text)` + padding: var(--ads-v2-spaces-1) 0; + color: var(--ads-v2-color-fg-muted); +`; diff --git a/app/client/packages/design-system/ads/src/List/List.tsx b/app/client/packages/design-system/ads/src/List/List.tsx index 95b905e7e6..18240c8a78 100644 --- a/app/client/packages/design-system/ads/src/List/List.tsx +++ b/app/client/packages/design-system/ads/src/List/List.tsx @@ -4,8 +4,11 @@ import clsx from "classnames"; import type { ListItemProps, ListProps } from "./List.types"; import { BottomContentWrapper, + GroupedList, + GroupTitle, InlineDescriptionWrapper, RightControlWrapper, + StyledGroup, StyledList, StyledListItem, TooltipTextWrapper, @@ -24,12 +27,15 @@ import { } from "./List.constants"; import { useEventCallback } from "usehooks-ts"; -function List({ className, items, ...rest }: ListProps) { - return ( +function List({ children, className, groupTitle, ...rest }: ListProps) { + return groupTitle ? ( + + {groupTitle} + {children} + + ) : ( - {items.map((item) => { - return ; - })} + {children} ); } diff --git a/app/client/packages/design-system/ads/src/List/List.types.tsx b/app/client/packages/design-system/ads/src/List/List.types.tsx index 282d463f7b..e48ab57b19 100644 --- a/app/client/packages/design-system/ads/src/List/List.types.tsx +++ b/app/client/packages/design-system/ads/src/List/List.types.tsx @@ -39,7 +39,8 @@ export interface ListItemProps { } export interface ListProps { - items: ListItemProps[]; className?: string; id?: string; + children: ReactNode | ReactNode[]; + groupTitle?: string; } diff --git a/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.stories.tsx b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.stories.tsx new file mode 100644 index 0000000000..0f0f6ad5ba --- /dev/null +++ b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.stories.tsx @@ -0,0 +1,185 @@ +/* eslint-disable no-console */ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { EntityGroupsList, EntityGroup } from "./EntityGroupsList"; +import type { + EntityGroupsListProps, + EntityGroupProps, +} from "./EntityGroupsList.types"; +import { Icon } from "../../../Icon"; + +const meta: Meta = { + title: "ADS/Templates/Entity Explorer/Entity Groups List", + component: EntityGroupsList, +}; + +export default meta; + +const EntityGroupTemplate = (props: EntityGroupProps) => { + const { addConfig, className, groupTitle, items } = props; + + return ; +}; + +export const SingleGroupWithAddNLazyLoad = EntityGroupTemplate.bind( + {}, +) as StoryObj; + +SingleGroupWithAddNLazyLoad.args = { + groupTitle: "Datasources", + className: "t--from-source-list", + items: [ + { + startIcon: , + className: "t--datasource-create-option-users", + title: "Users", + onClick: () => console.log("Users clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-movies", + title: "Movies", + onClick: () => console.log("Movies clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-untitled_datasource_1", + title: "Untitled datasource 1", + onClick: () => console.log("Untitled datasource 1 clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-untitled_datasource_2", + title: "Untitled datasource 2", + onClick: () => console.log("Untitled datasource 2 clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-untitled_datasource_3", + title: "Untitled datasource 3", + onClick: () => console.log("Untitled datasource 3 clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-untitled_datasource_4", + title: "Untitled datasource 4", + onClick: () => console.log("Untitled datasource 4 clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-users_(1)", + title: "Users (1)", + onClick: () => console.log("Users(1) clicked"), + }, + ], + addConfig: { + icon: , + title: "New datasource", + onClick: () => console.log("New datasource clicked"), + }, +}; + +const EntityGroupsListTemplate = (props: EntityGroupsListProps) => { + const { groups } = props; + + return ; +}; + +export const MultipleGroupsWithAddNLazyLoad = EntityGroupsListTemplate.bind( + {}, +) as StoryObj; + +MultipleGroupsWithAddNLazyLoad.args = { + groups: [ + { + groupTitle: "Datasources", + className: "t--from-source-list", + items: [ + { + startIcon: , + className: "t--datasource-create-option-users", + title: "Users", + onClick: () => console.log("Users clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-movies", + title: "Movies", + onClick: () => console.log("Movies clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-untitled_datasource_1", + title: "Untitled datasource 1", + onClick: () => console.log("Untitled datasource 1 clicked"), + }, + ], + addConfig: { + icon: , + title: "New datasource", + onClick: () => console.log("New datasource clicked"), + }, + }, + { + groupTitle: "Apis", + className: "t--from-source-list", + items: [ + { + startIcon: , + className: "t--datasource-create-option-users", + title: "Users", + onClick: () => console.log("Users clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-movies", + title: "Movies", + onClick: () => console.log("Movies clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-untitled_datasource_1", + title: "Untitled datasource 1", + onClick: () => console.log("Untitled datasource 1 clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-untitled_datasource_2", + title: "Untitled datasource 2", + onClick: () => console.log("Untitled datasource 2 clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-untitled_datasource_3", + title: "Untitled datasource 3", + onClick: () => console.log("Untitled datasource 3 clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-untitled_datasource_4", + title: "Untitled datasource 4", + onClick: () => console.log("Untitled datasource 4 clicked"), + }, + ], + }, + { + groupTitle: "Apis", + className: "t--from-source-list", + items: [ + { + startIcon: , + className: "t--datasource-create-option-users", + title: "Users", + onClick: () => console.log("Users clicked"), + }, + { + startIcon: , + className: "t--datasource-create-option-movies", + title: "Movies", + onClick: () => console.log("Movies clicked"), + }, + ], + }, + ], +}; diff --git a/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.styles.ts b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.styles.ts new file mode 100644 index 0000000000..6e6e06b316 --- /dev/null +++ b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.styles.ts @@ -0,0 +1,8 @@ +import styled from "styled-components"; +import { ListItem } from "../../../List"; + +export const LoadMore = styled(ListItem)` + .ads-v2-listitem__title { + color: var(--ads-v2-color-fg-subtle); + } +`; diff --git a/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.tsx b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.tsx new file mode 100644 index 0000000000..9493867fa5 --- /dev/null +++ b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.tsx @@ -0,0 +1,80 @@ +import React, { useMemo } from "react"; +import { LoadMore } from "./EntityGroupsList.styles"; +import type { + EntityGroupProps, + EntityGroupsListProps, +} from "./EntityGroupsList.types"; +import { Flex } from "../../../Flex"; +import { List, ListItem, type ListItemProps } from "../../../List"; +import { Divider } from "../../../Divider"; + +const EntityGroupsList = (props: EntityGroupsListProps) => { + const { flexProps, groups, showDivider } = props; + + return ( + + {groups.map((group, index) => ( + + + {showDivider && index < groups.length - 1 && ( + + )} + + ))} + + ); +}; + +const EntityGroup = ({ group }: { group: EntityGroupProps }) => { + const [visibleItemsCount, setVisibleItemsCount] = React.useState(5); + + const lazyLoading = useMemo(() => { + return { + visibleItemsCount, + hasMore: visibleItemsCount < group.items.length, + handleLoadMore: () => setVisibleItemsCount(group.items.length), + }; + }, [visibleItemsCount, group.items.length]); + + const updatedGroup = lazyLoading.hasMore + ? { + ...group, + items: group.items.slice(0, lazyLoading.visibleItemsCount), + } + : group; + + return ( + + + {updatedGroup.items.map((item: T, index) => + group.renderList ? ( + group.renderList(item) + ) : ( + + ), + )} + + {lazyLoading?.hasMore && ( + + )} + {group.addConfig && ( + + )} + + ); +}; + +export { EntityGroup, EntityGroupsList }; diff --git a/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.types.ts b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.types.ts new file mode 100644 index 0000000000..84fdad7103 --- /dev/null +++ b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/EntityGroupsList.types.ts @@ -0,0 +1,19 @@ +import type { MouseEvent, ReactNode } from "react"; +import type { FlexProps } from "../../../Flex"; +export interface EntityGroupProps { + groupTitle: string; + className: string; + items: T[]; + addConfig?: { + icon: ReactNode; + title: string; + onClick: (e: MouseEvent) => void; + }; + renderList?: (item: T) => React.ReactNode; +} + +export interface EntityGroupsListProps { + groups: EntityGroupProps[]; + flexProps?: FlexProps; + showDivider?: boolean; +} diff --git a/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/index.ts b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/index.ts new file mode 100644 index 0000000000..abd7595e14 --- /dev/null +++ b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityGroupsList/index.ts @@ -0,0 +1,5 @@ +export { EntityGroup, EntityGroupsList } from "./EntityGroupsList"; +export type { + EntityGroupProps, + EntityGroupsListProps, +} from "./EntityGroupsList.types"; diff --git a/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityItem/index.ts b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityItem/index.ts index 7f3dc03d3f..b857f670eb 100644 --- a/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityItem/index.ts +++ b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/EntityItem/index.ts @@ -1 +1,2 @@ export { EntityItem } from "./EntityItem"; +export type { EntityItemProps } from "./EntityItem.types"; diff --git a/app/client/packages/design-system/ads/src/Templates/EntityExplorer/index.ts b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/index.ts index 51c3e9097e..d4ea2b6409 100644 --- a/app/client/packages/design-system/ads/src/Templates/EntityExplorer/index.ts +++ b/app/client/packages/design-system/ads/src/Templates/EntityExplorer/index.ts @@ -7,3 +7,4 @@ export { NoSearchResults } from "./NoSearchResults"; export * from "./ExplorerContainer"; export * from "./EntityItem"; export { useEditableText } from "./Editable"; +export * from "./EntityGroupsList"; diff --git a/app/client/packages/design-system/ads/src/Templates/IDEHeader/IDEHeader.stories.tsx b/app/client/packages/design-system/ads/src/Templates/IDEHeader/IDEHeader.stories.tsx index c58c1d8776..9afd4a4666 100644 --- a/app/client/packages/design-system/ads/src/Templates/IDEHeader/IDEHeader.stories.tsx +++ b/app/client/packages/design-system/ads/src/Templates/IDEHeader/IDEHeader.stories.tsx @@ -6,7 +6,7 @@ import { IDEHeaderSwitcher } from "./HeaderSwitcher"; import { noop } from "lodash"; import { Icon } from "../../Icon"; import { Button } from "../../Button"; -import { List } from "../../List"; +import { List, ListItem } from "../../List"; import { Flex } from "../../Flex"; import { Text } from "../../Text"; import { ListHeaderContainer } from "../EntityExplorer/styles"; @@ -58,6 +58,16 @@ export const WithHeaderTitle = () => { export const WithHeaderDropdown = () => { const [open, setOpen] = React.useState(false); + const items = [ + { + title: "Page1", + onClick: noop, + }, + { + title: "Page2", + onClick: noop, + }, + ]; return ( @@ -79,18 +89,11 @@ export const WithHeaderDropdown = () => { Pages