feat: Updated new tab ui with search and load more (#34981)
## Description Updated new tab ui with search, load more and updated titles. Fixes #34809, #34530 ## Automation /ok-to-test tags="@tag.All" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/10036807357> > Commit: 2d37c01455f48de14510fa17cae104dd7578091c > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10036807357&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.All` > Spec: > <hr>Mon, 22 Jul 2024 08:46:24 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Added search functionality in the JS and Query Editor Panes to filter and display grouped items based on search terms. - Introduced a new component to render grouped lists with dynamic load more functionality. - **Enhancements** - Improved text and terminology for clearer user understanding in various modules. - Enhanced layout alignment by adjusting component heights to account for editor tabs. - **Refactor** - Improved type safety and refined logic in multiple functions for better performance and maintainability. - Replaced hardcoded values with constants for consistent and easier management. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
a38ef9a096
commit
5127005129
|
|
@ -2312,10 +2312,17 @@ export const EDITOR_PANE_TEXTS = {
|
||||||
query_create_tab_title: () => "Create new query from",
|
query_create_tab_title: () => "Create new query from",
|
||||||
widgets_create_tab_title: () => "Drag & drop UI elements",
|
widgets_create_tab_title: () => "Drag & drop UI elements",
|
||||||
js_create_tab_title: () => "Create JS object from",
|
js_create_tab_title: () => "Create JS object from",
|
||||||
queries_create_from_existing: () => "From existing datasource",
|
js_create_modules: () => "JS modules (Beta)",
|
||||||
queries_create_new: () => "New API",
|
queries_create_from_existing: () => "Datasources",
|
||||||
|
queries_create_new: () => "Quick actions",
|
||||||
|
queries_create_modules: () => "Query modules (Beta)",
|
||||||
loading_building_blocks: () => "Loading building blocks",
|
loading_building_blocks: () => "Loading building blocks",
|
||||||
empty_search_result: (type: string) => `No ${type} match your search`,
|
empty_search_result: (type: string) => `No ${type} match your search`,
|
||||||
|
search_objects: {
|
||||||
|
jsObject: () => "JS object",
|
||||||
|
queries: () => "queries",
|
||||||
|
datasources: () => "datasources",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PARTIAL_IMPORT_EXPORT = {
|
export const PARTIAL_IMPORT_EXPORT = {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import history from "utils/history";
|
import history from "utils/history";
|
||||||
import { useLocation } from "react-router";
|
import { useLocation } from "react-router";
|
||||||
import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity";
|
import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity";
|
||||||
|
|
@ -33,7 +33,7 @@ import ListQuery from "pages/Editor/IDE/EditorPane/Query/List";
|
||||||
import type { AppState } from "@appsmith/reducers";
|
import type { AppState } from "@appsmith/reducers";
|
||||||
import keyBy from "lodash/keyBy";
|
import keyBy from "lodash/keyBy";
|
||||||
import { getPluginEntityIcon } from "pages/Editor/Explorer/ExplorerIcons";
|
import { getPluginEntityIcon } from "pages/Editor/Explorer/ExplorerIcons";
|
||||||
import { Tag, type ListItemProps } from "design-system";
|
import type { ListItemProps } from "design-system";
|
||||||
import { useCurrentEditorState } from "pages/Editor/IDE/hooks";
|
import { useCurrentEditorState } from "pages/Editor/IDE/hooks";
|
||||||
import { createAddClassName } from "pages/Editor/IDE/EditorPane/utils";
|
import { createAddClassName } from "pages/Editor/IDE/EditorPane/utils";
|
||||||
import { QueriesBlankState } from "pages/Editor/QueryEditor/QueriesBlankState";
|
import { QueriesBlankState } from "pages/Editor/QueryEditor/QueriesBlankState";
|
||||||
|
|
@ -182,12 +182,16 @@ export const useAddQueryListItems = () => {
|
||||||
[pageId, dispatch],
|
[pageId, dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
const getListItems = (data: any[]) => {
|
const getListItems = (data: ActionOperation[]) => {
|
||||||
return data.map((fileOperation) => {
|
return data.map((fileOperation) => {
|
||||||
const title =
|
let title =
|
||||||
fileOperation.entityExplorerTitle ||
|
fileOperation.entityExplorerTitle ||
|
||||||
fileOperation.dsName ||
|
fileOperation.dsName ||
|
||||||
fileOperation.title;
|
fileOperation.title;
|
||||||
|
title =
|
||||||
|
fileOperation.focusEntityType === FocusEntity.QUERY_MODULE_INSTANCE
|
||||||
|
? fileOperation.title
|
||||||
|
: title;
|
||||||
const className = createAddClassName(title);
|
const className = createAddClassName(title);
|
||||||
const icon =
|
const icon =
|
||||||
fileOperation.icon ||
|
fileOperation.icon ||
|
||||||
|
|
@ -197,11 +201,10 @@ export const useAddQueryListItems = () => {
|
||||||
startIcon: icon,
|
startIcon: icon,
|
||||||
wrapperClassName: className,
|
wrapperClassName: className,
|
||||||
title,
|
title,
|
||||||
description: !!fileOperation.isBeta ? (
|
description:
|
||||||
<Tag isClosable={false}>Beta</Tag>
|
fileOperation.focusEntityType === FocusEntity.QUERY_MODULE_INSTANCE
|
||||||
) : (
|
? fileOperation.dsName
|
||||||
""
|
: "",
|
||||||
),
|
|
||||||
descriptionType: "inline",
|
descriptionType: "inline",
|
||||||
onClick: onCreateItemClick.bind(null, fileOperation),
|
onClick: onCreateItemClick.bind(null, fileOperation),
|
||||||
} as ListItemProps;
|
} as ListItemProps;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { useCallback } from "react";
|
import React, { useCallback, useState } from "react";
|
||||||
import SegmentAddHeader from "../components/SegmentAddHeader";
|
import SegmentAddHeader from "../components/SegmentAddHeader";
|
||||||
import { EDITOR_PANE_TEXTS } from "@appsmith/constants/messages";
|
import { EDITOR_PANE_TEXTS, createMessage } from "@appsmith/constants/messages";
|
||||||
import type { ListItemProps } from "design-system";
|
import type { ListItemProps } from "design-system";
|
||||||
import { Flex, Tag } from "design-system";
|
import { Flex, SearchInput } from "design-system";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { getCurrentPageId } from "selectors/editorSelectors";
|
import { getCurrentPageId } from "selectors/editorSelectors";
|
||||||
import GroupedList from "../components/GroupedList";
|
import GroupedList from "../components/GroupedList";
|
||||||
|
|
@ -12,11 +12,15 @@ import {
|
||||||
} from "@appsmith/pages/Editor/IDE/EditorPane/JS/hooks";
|
} from "@appsmith/pages/Editor/IDE/EditorPane/JS/hooks";
|
||||||
import type { ActionOperation } from "components/editorComponents/GlobalSearch/utils";
|
import type { ActionOperation } from "components/editorComponents/GlobalSearch/utils";
|
||||||
import type { AddProps } from "../types/AddProps";
|
import type { AddProps } from "../types/AddProps";
|
||||||
import { createAddClassName } from "../utils";
|
import { createAddClassName, fuzzySearchInObjectItems } from "../utils";
|
||||||
|
import { FocusEntity } from "navigation/FocusEntity";
|
||||||
|
import type { GroupedListProps } from "../components/types";
|
||||||
|
import { EmptySearchResult } from "../components/EmptySearchResult";
|
||||||
|
|
||||||
const AddJS = ({ containerProps, innerContainerProps }: AddProps) => {
|
const AddJS = ({ containerProps, innerContainerProps }: AddProps) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const pageId = useSelector(getCurrentPageId);
|
const pageId = useSelector(getCurrentPageId);
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
|
||||||
const groupedJsOperations = useGroupedAddJsOperations();
|
const groupedJsOperations = useGroupedAddJsOperations();
|
||||||
|
|
||||||
|
|
@ -35,13 +39,29 @@ const AddJS = ({ containerProps, innerContainerProps }: AddProps) => {
|
||||||
return {
|
return {
|
||||||
startIcon: data.icon,
|
startIcon: data.icon,
|
||||||
title,
|
title,
|
||||||
description: !!data.isBeta ? <Tag isClosable={false}>Beta</Tag> : "",
|
description:
|
||||||
|
data.focusEntityType === FocusEntity.JS_MODULE_INSTANCE
|
||||||
|
? data.dsName
|
||||||
|
: "",
|
||||||
descriptionType: "inline",
|
descriptionType: "inline",
|
||||||
onClick: onCreateItemClick.bind(null, data),
|
onClick: onCreateItemClick.bind(null, data),
|
||||||
wrapperClassName: createAddClassName(title),
|
wrapperClassName: createAddClassName(title),
|
||||||
} as ListItemProps;
|
} as ListItemProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const groups = groupedJsOperations.map(
|
||||||
|
({ className, operations, title }) => ({
|
||||||
|
groupTitle: title,
|
||||||
|
className: className,
|
||||||
|
items: operations.map(getListItems),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const localGroups = fuzzySearchInObjectItems<GroupedListProps[]>(
|
||||||
|
searchTerm,
|
||||||
|
groups,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
data-testid="t--ide-add-pane"
|
data-testid="t--ide-add-pane"
|
||||||
|
|
@ -61,14 +81,13 @@ const AddJS = ({ containerProps, innerContainerProps }: AddProps) => {
|
||||||
onCloseClick={closeAddJS}
|
onCloseClick={closeAddJS}
|
||||||
titleMessage={EDITOR_PANE_TEXTS.js_create_tab_title}
|
titleMessage={EDITOR_PANE_TEXTS.js_create_tab_title}
|
||||||
/>
|
/>
|
||||||
|
<SearchInput onChange={setSearchTerm} value={searchTerm} />
|
||||||
<GroupedList
|
{localGroups.length > 0 ? <GroupedList groups={localGroups} /> : null}
|
||||||
groups={groupedJsOperations.map((op) => ({
|
{localGroups.length === 0 && searchTerm !== "" ? (
|
||||||
groupTitle: op.title,
|
<EmptySearchResult
|
||||||
className: op.className,
|
type={createMessage(EDITOR_PANE_TEXTS.search_objects.jsObject)}
|
||||||
items: op.operations.map(getListItems),
|
/>
|
||||||
}))}
|
) : null}
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { useSelector } from "react-redux";
|
||||||
import { Flex, Text } from "design-system";
|
import { Flex, Text } from "design-system";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import type { EditorSegmentList } from "@appsmith/selectors/appIDESelectors";
|
||||||
import { selectJSSegmentEditorList } from "@appsmith/selectors/appIDESelectors";
|
import { selectJSSegmentEditorList } from "@appsmith/selectors/appIDESelectors";
|
||||||
import { useActiveAction } from "@appsmith/pages/Editor/Explorer/hooks";
|
import { useActiveAction } from "@appsmith/pages/Editor/Explorer/hooks";
|
||||||
import {
|
import {
|
||||||
|
|
@ -19,7 +20,8 @@ import { useJSAdd } from "@appsmith/pages/Editor/IDE/EditorPane/JS/hooks";
|
||||||
import { JSListItem } from "@appsmith/pages/Editor/IDE/EditorPane/JS/ListItem";
|
import { JSListItem } from "@appsmith/pages/Editor/IDE/EditorPane/JS/ListItem";
|
||||||
import { BlankState } from "./BlankState";
|
import { BlankState } from "./BlankState";
|
||||||
import { AddAndSearchbar } from "../components/AddAndSearchbar";
|
import { AddAndSearchbar } from "../components/AddAndSearchbar";
|
||||||
import { fuzzySearchInFiles } from "../utils";
|
import { fuzzySearchInObjectItems } from "../utils";
|
||||||
|
import { EmptySearchResult } from "../components/EmptySearchResult";
|
||||||
import { EDITOR_PANE_TEXTS, createMessage } from "@appsmith/constants/messages";
|
import { EDITOR_PANE_TEXTS, createMessage } from "@appsmith/constants/messages";
|
||||||
|
|
||||||
const JSContainer = styled(Flex)`
|
const JSContainer = styled(Flex)`
|
||||||
|
|
@ -44,7 +46,10 @@ const ListJSObjects = () => {
|
||||||
|
|
||||||
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
||||||
|
|
||||||
const localFiles = fuzzySearchInFiles(searchTerm, files);
|
const localFiles = fuzzySearchInObjectItems<EditorSegmentList>(
|
||||||
|
searchTerm,
|
||||||
|
files,
|
||||||
|
);
|
||||||
|
|
||||||
const canCreateActions = getHasCreateActionPermission(
|
const canCreateActions = getHasCreateActionPermission(
|
||||||
isFeatureEnabled,
|
isFeatureEnabled,
|
||||||
|
|
@ -112,13 +117,9 @@ const ListJSObjects = () => {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{localFiles.length === 0 && searchTerm !== "" ? (
|
{localFiles.length === 0 && searchTerm !== "" ? (
|
||||||
<Text
|
<EmptySearchResult
|
||||||
className="font-normal text-center"
|
type={createMessage(EDITOR_PANE_TEXTS.search_objects.jsObject)}
|
||||||
color="var(--ads-v2-color-fg-muted)"
|
/>
|
||||||
kind="body-s"
|
|
||||||
>
|
|
||||||
{createMessage(EDITOR_PANE_TEXTS.empty_search_result, "JS")}
|
|
||||||
</Text>
|
|
||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
</FilesContextProvider>
|
</FilesContextProvider>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { Flex } from "design-system";
|
import { Flex, SearchInput } from "design-system";
|
||||||
|
|
||||||
import { EDITOR_PANE_TEXTS } from "@appsmith/constants/messages";
|
import { EDITOR_PANE_TEXTS, createMessage } from "@appsmith/constants/messages";
|
||||||
import SegmentAddHeader from "../components/SegmentAddHeader";
|
import SegmentAddHeader from "../components/SegmentAddHeader";
|
||||||
import GroupedList from "../components/GroupedList";
|
import GroupedList from "../components/GroupedList";
|
||||||
import {
|
import {
|
||||||
|
|
@ -10,12 +10,27 @@ import {
|
||||||
useQueryAdd,
|
useQueryAdd,
|
||||||
} from "@appsmith/pages/Editor/IDE/EditorPane/Query/hooks";
|
} from "@appsmith/pages/Editor/IDE/EditorPane/Query/hooks";
|
||||||
import type { AddProps } from "../types/AddProps";
|
import type { AddProps } from "../types/AddProps";
|
||||||
|
import { fuzzySearchInObjectItems } from "../utils";
|
||||||
|
import type { GroupedListProps } from "../components/types";
|
||||||
|
import { EmptySearchResult } from "../components/EmptySearchResult";
|
||||||
|
|
||||||
const AddQuery = ({ containerProps, innerContainerProps }: AddProps) => {
|
const AddQuery = ({ containerProps, innerContainerProps }: AddProps) => {
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const { getListItems } = useAddQueryListItems();
|
const { getListItems } = useAddQueryListItems();
|
||||||
const groupedActionOperations = useGroupedAddQueryOperations();
|
const groupedActionOperations = useGroupedAddQueryOperations();
|
||||||
const { closeAddQuery } = useQueryAdd();
|
const { closeAddQuery } = useQueryAdd();
|
||||||
|
|
||||||
|
const groups = groupedActionOperations.map((group) => ({
|
||||||
|
groupTitle: group.title,
|
||||||
|
className: group.className,
|
||||||
|
items: getListItems(group.operations),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const localGroups = fuzzySearchInObjectItems<GroupedListProps[]>(
|
||||||
|
searchTerm,
|
||||||
|
groups,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
data-testid="t--ide-add-pane"
|
data-testid="t--ide-add-pane"
|
||||||
|
|
@ -35,13 +50,13 @@ const AddQuery = ({ containerProps, innerContainerProps }: AddProps) => {
|
||||||
onCloseClick={closeAddQuery}
|
onCloseClick={closeAddQuery}
|
||||||
titleMessage={EDITOR_PANE_TEXTS.query_create_tab_title}
|
titleMessage={EDITOR_PANE_TEXTS.query_create_tab_title}
|
||||||
/>
|
/>
|
||||||
<GroupedList
|
<SearchInput autofocus onChange={setSearchTerm} value={searchTerm} />
|
||||||
groups={groupedActionOperations.map((group) => ({
|
{localGroups.length > 0 ? <GroupedList groups={localGroups} /> : null}
|
||||||
groupTitle: group.title,
|
{localGroups.length === 0 && searchTerm !== "" ? (
|
||||||
className: group.className,
|
<EmptySearchResult
|
||||||
items: getListItems(group.operations),
|
type={createMessage(EDITOR_PANE_TEXTS.search_objects.datasources)}
|
||||||
}))}
|
/>
|
||||||
/>
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||||
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
||||||
import { getCurrentPageId } from "@appsmith/selectors/entitiesSelector";
|
import { getCurrentPageId } from "@appsmith/selectors/entitiesSelector";
|
||||||
|
import type { EditorSegmentList } from "@appsmith/selectors/appIDESelectors";
|
||||||
import { selectQuerySegmentEditorList } from "@appsmith/selectors/appIDESelectors";
|
import { selectQuerySegmentEditorList } from "@appsmith/selectors/appIDESelectors";
|
||||||
import { ActionParentEntityType } from "@appsmith/entities/Engine/actionHelpers";
|
import { ActionParentEntityType } from "@appsmith/entities/Engine/actionHelpers";
|
||||||
import { FilesContextProvider } from "pages/Editor/Explorer/Files/FilesContextProvider";
|
import { FilesContextProvider } from "pages/Editor/Explorer/Files/FilesContextProvider";
|
||||||
|
|
@ -19,7 +20,8 @@ import { QueryListItem } from "@appsmith/pages/Editor/IDE/EditorPane/Query/ListI
|
||||||
import { getShowWorkflowFeature } from "@appsmith/selectors/workflowSelectors";
|
import { getShowWorkflowFeature } from "@appsmith/selectors/workflowSelectors";
|
||||||
import { BlankState } from "./BlankState";
|
import { BlankState } from "./BlankState";
|
||||||
import { AddAndSearchbar } from "../components/AddAndSearchbar";
|
import { AddAndSearchbar } from "../components/AddAndSearchbar";
|
||||||
import { fuzzySearchInFiles } from "../utils";
|
import { fuzzySearchInObjectItems } from "../utils";
|
||||||
|
import { EmptySearchResult } from "../components/EmptySearchResult";
|
||||||
import { EDITOR_PANE_TEXTS, createMessage } from "@appsmith/constants/messages";
|
import { EDITOR_PANE_TEXTS, createMessage } from "@appsmith/constants/messages";
|
||||||
|
|
||||||
const ListQuery = () => {
|
const ListQuery = () => {
|
||||||
|
|
@ -30,7 +32,10 @@ const ListQuery = () => {
|
||||||
const pagePermissions = useSelector(getPagePermissions);
|
const pagePermissions = useSelector(getPagePermissions);
|
||||||
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
||||||
|
|
||||||
const localFiles = fuzzySearchInFiles(searchTerm, files);
|
const localFiles = fuzzySearchInObjectItems<EditorSegmentList>(
|
||||||
|
searchTerm,
|
||||||
|
files,
|
||||||
|
);
|
||||||
|
|
||||||
const canCreateActions = getHasCreateActionPermission(
|
const canCreateActions = getHasCreateActionPermission(
|
||||||
isFeatureEnabled,
|
isFeatureEnabled,
|
||||||
|
|
@ -92,13 +97,9 @@ const ListQuery = () => {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{localFiles.length === 0 && searchTerm !== "" ? (
|
{localFiles.length === 0 && searchTerm !== "" ? (
|
||||||
<Text
|
<EmptySearchResult
|
||||||
className="font-normal text-center"
|
type={createMessage(EDITOR_PANE_TEXTS.search_objects.jsObject)}
|
||||||
color="var(--ads-v2-color-fg-muted)"
|
/>
|
||||||
kind="body-s"
|
|
||||||
>
|
|
||||||
{createMessage(EDITOR_PANE_TEXTS.empty_search_result, "queries")}
|
|
||||||
</Text>
|
|
||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Text } from "design-system";
|
||||||
|
import { EDITOR_PANE_TEXTS, createMessage } from "@appsmith/constants/messages";
|
||||||
|
|
||||||
|
const EmptySearchResult = ({ type }: { type: string }) => {
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
className="font-normal text-center"
|
||||||
|
color="var(--ads-v2-color-fg-muted)"
|
||||||
|
kind="body-s"
|
||||||
|
>
|
||||||
|
{createMessage(EDITOR_PANE_TEXTS.empty_search_result, type)}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { EmptySearchResult };
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
import React, { useMemo, useState } from "react";
|
||||||
|
import type { GroupedListProps } from "./types";
|
||||||
|
import { DEFAULT_GROUP_LIST_SIZE } from "./constants";
|
||||||
|
import { Flex, List, Text } from "design-system";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
interface GroupProps {
|
||||||
|
group: GroupedListProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyledList = styled(List)`
|
||||||
|
padding: 0;
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
& .ds-load-more .ads-v2-listitem__title {
|
||||||
|
--color: var(--ads-v2-color-fg-subtle);
|
||||||
|
}
|
||||||
|
& .ads-v2-listitem__wrapper .ads-v2-listitem__idesc {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .ads-v2-listitem__wrapper:hover .ads-v2-listitem__idesc {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Group: React.FC<GroupProps> = ({ group }) => {
|
||||||
|
const [visibleItemsCount, setVisibleItemsCount] = useState<number>(
|
||||||
|
DEFAULT_GROUP_LIST_SIZE,
|
||||||
|
);
|
||||||
|
const { className, groupTitle, items: groupItems } = group;
|
||||||
|
|
||||||
|
const items = useMemo(() => {
|
||||||
|
const items = groupItems.slice(0, visibleItemsCount);
|
||||||
|
const hasMoreItems = groupItems.length > visibleItemsCount;
|
||||||
|
|
||||||
|
const handleLoadMore = () => {
|
||||||
|
setVisibleItemsCount(groupItems.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hasMoreItems) {
|
||||||
|
items.push({
|
||||||
|
title: "Load more...",
|
||||||
|
description: "",
|
||||||
|
descriptionType: "inline",
|
||||||
|
onClick: handleLoadMore,
|
||||||
|
className: "ds-load-more",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: try to avoid this
|
||||||
|
if (hasMoreItems && groupTitle === "Datasources") {
|
||||||
|
items.push(groupItems[groupItems.length - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}, [groupItems, visibleItemsCount, groupTitle]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
borderBottom="1px solid var(--ads-v2-color-border-muted)"
|
||||||
|
className="groups-list-group"
|
||||||
|
flexDirection="column"
|
||||||
|
key={groupTitle}
|
||||||
|
pb="spaces-3"
|
||||||
|
>
|
||||||
|
{groupTitle ? (
|
||||||
|
<Text
|
||||||
|
className="px-0 py-[var(--ads-v2-spaces-1)]"
|
||||||
|
color="var(--ads-v2-color-fg-muted)"
|
||||||
|
kind="body-s"
|
||||||
|
>
|
||||||
|
{groupTitle}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
<StyledList className={className} items={items} />
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Group };
|
||||||
|
|
@ -1,27 +1,24 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import type { FlexProps } from "design-system";
|
||||||
|
import { Flex } from "design-system";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import type { FlexProps, ListItemProps } from "design-system";
|
import type { GroupedListProps } from "./types";
|
||||||
import { Flex, List, Text } from "design-system";
|
import { Group } from "./Group";
|
||||||
|
|
||||||
const StyledList = styled(List)`
|
|
||||||
padding: 0;
|
|
||||||
gap: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export type GroupedListProps = Array<{
|
|
||||||
groupTitle?: string;
|
|
||||||
className: string;
|
|
||||||
items: ListItemProps[];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
groups: GroupedListProps;
|
groups: GroupedListProps[];
|
||||||
flexProps?: FlexProps;
|
flexProps?: FlexProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const StyledFlex = styled(Flex)`
|
||||||
|
& .groups-list-group:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const GroupedList = (props: Props) => {
|
const GroupedList = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<Flex
|
<StyledFlex
|
||||||
flex="1"
|
flex="1"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
gap="spaces-4"
|
gap="spaces-4"
|
||||||
|
|
@ -29,20 +26,9 @@ const GroupedList = (props: Props) => {
|
||||||
{...props.flexProps}
|
{...props.flexProps}
|
||||||
>
|
>
|
||||||
{props.groups.map((group) => (
|
{props.groups.map((group) => (
|
||||||
<Flex flexDirection="column" key={group.groupTitle}>
|
<Group group={group} key={group.groupTitle} />
|
||||||
{group.groupTitle ? (
|
|
||||||
<Text
|
|
||||||
className="px-0 py-[var(--ads-v2-spaces-1)]"
|
|
||||||
color="var(--ads-v2-color-fg-muted)"
|
|
||||||
kind="body-s"
|
|
||||||
>
|
|
||||||
{group.groupTitle}
|
|
||||||
</Text>
|
|
||||||
) : null}
|
|
||||||
<StyledList className={group.className} items={group.items} />
|
|
||||||
</Flex>
|
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</StyledFlex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ const SegmentAddHeader = (props: Props) => {
|
||||||
: "var(--ads-v2-color-gray-50)"
|
: "var(--ads-v2-color-gray-50)"
|
||||||
}
|
}
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
py="spaces-2"
|
|
||||||
>
|
>
|
||||||
<Text color="var(--ads-v2-color-fg)" kind="heading-xs">
|
<Text color="var(--ads-v2-color-fg)" kind="heading-xs">
|
||||||
{createMessage(props.titleMessage)}
|
{createMessage(props.titleMessage)}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export const DEFAULT_GROUP_LIST_SIZE = 5;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import type { ListItemProps } from "design-system";
|
||||||
|
|
||||||
|
export interface GroupedListProps {
|
||||||
|
groupTitle?: string;
|
||||||
|
className: string;
|
||||||
|
items: ListItemProps[];
|
||||||
|
}
|
||||||
1
app/client/src/pages/Editor/IDE/EditorPane/constants.ts
Normal file
1
app/client/src/pages/Editor/IDE/EditorPane/constants.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export const EDITOR_TABS_HEIGHT = "32px";
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { EditorSegmentList } from "@appsmith/selectors/appIDESelectors";
|
import type { EditorSegmentList } from "@appsmith/selectors/appIDESelectors";
|
||||||
import { fuzzySearchInFiles } from "./utils";
|
import { fuzzySearchInObjectItems } from "./utils";
|
||||||
import { PluginType } from "entities/Action";
|
import { PluginType } from "entities/Action";
|
||||||
|
|
||||||
const sampleFiles: EditorSegmentList = [
|
const sampleFiles: EditorSegmentList = [
|
||||||
|
|
@ -19,14 +19,14 @@ const sampleFiles: EditorSegmentList = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
describe("fuzzySearchInFiles", () => {
|
describe("fuzzySearchInObjectItems", () => {
|
||||||
it("should return all files when the search string is empty", () => {
|
it("should return all files when the search string is empty", () => {
|
||||||
const result = fuzzySearchInFiles("", sampleFiles);
|
const result = fuzzySearchInObjectItems("", sampleFiles);
|
||||||
expect(result).toEqual(sampleFiles);
|
expect(result).toEqual(sampleFiles);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return the correct file when the search string exactly matches a file title", () => {
|
it("should return the correct file when the search string exactly matches a file title", () => {
|
||||||
const result = fuzzySearchInFiles("file1", sampleFiles);
|
const result = fuzzySearchInObjectItems("file1", sampleFiles);
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
{
|
{
|
||||||
group: "Group 1",
|
group: "Group 1",
|
||||||
|
|
@ -36,12 +36,12 @@ describe("fuzzySearchInFiles", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return an empty array when no files match the search string", () => {
|
it("should return an empty array when no files match the search string", () => {
|
||||||
const result = fuzzySearchInFiles("nonexistentfile", sampleFiles);
|
const result = fuzzySearchInObjectItems("nonexistentfile", sampleFiles);
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return all files containing the common substring in their titles", () => {
|
it("should return all files containing the common substring in their titles", () => {
|
||||||
const result = fuzzySearchInFiles("file", sampleFiles);
|
const result = fuzzySearchInObjectItems("file", sampleFiles);
|
||||||
expect(result).toEqual(sampleFiles);
|
expect(result).toEqual(sampleFiles);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import type { EditorSegmentList } from "@appsmith/selectors/appIDESelectors";
|
|
||||||
|
|
||||||
export const createAddClassName = (name: string) => {
|
export const createAddClassName = (name: string) => {
|
||||||
return "t--datasoucre-create-option-" + name.toLowerCase().replace(/ /g, "_");
|
return "t--datasoucre-create-option-" + name.toLowerCase().replace(/ /g, "_");
|
||||||
|
|
@ -11,19 +10,20 @@ const FUSE_OPTIONS = {
|
||||||
keys: ["title"],
|
keys: ["title"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fuzzySearchInFiles = (
|
export const fuzzySearchInObjectItems = <T extends any[]>(
|
||||||
searchStr: string,
|
searchStr: string,
|
||||||
files: EditorSegmentList,
|
files: T,
|
||||||
) => {
|
): T => {
|
||||||
if (searchStr && searchStr !== "") {
|
if (searchStr && searchStr !== "") {
|
||||||
const newFiles = files
|
const newFiles = files
|
||||||
.map((group) => {
|
.map((group) => {
|
||||||
const fuse = new Fuse(group.items, FUSE_OPTIONS);
|
const items = group["items"];
|
||||||
|
const fuse = new Fuse(items, FUSE_OPTIONS);
|
||||||
const resultItems = fuse.search(searchStr);
|
const resultItems = fuse.search(searchStr);
|
||||||
return { ...group, items: resultItems };
|
return { ...group, items: resultItems };
|
||||||
})
|
})
|
||||||
.filter((group) => group.items.length > 0);
|
.filter((group) => group.items.length > 0);
|
||||||
return newFiles;
|
return newFiles as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Flex } from "design-system";
|
import { Flex } from "design-system";
|
||||||
|
import { EDITOR_TABS_HEIGHT } from "../EditorPane/constants";
|
||||||
|
|
||||||
const Container = (props: { children: ReactNode }) => {
|
const Container = (props: { children: ReactNode }) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -10,8 +11,8 @@ const Container = (props: { children: ReactNode }) => {
|
||||||
borderBottom="1px solid var(--ads-v2-color-border-muted)"
|
borderBottom="1px solid var(--ads-v2-color-border-muted)"
|
||||||
gap="spaces-2"
|
gap="spaces-2"
|
||||||
id="ide-tabs-container"
|
id="ide-tabs-container"
|
||||||
maxHeight="32px"
|
maxHeight={EDITOR_TABS_HEIGHT}
|
||||||
minHeight="32px"
|
minHeight={EDITOR_TABS_HEIGHT}
|
||||||
px="spaces-2"
|
px="spaces-2"
|
||||||
width="100%"
|
width="100%"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Flex } from "design-system";
|
import { Flex } from "design-system";
|
||||||
import AddJS from "pages/Editor/IDE/EditorPane/JS/Add";
|
import AddJS from "pages/Editor/IDE/EditorPane/JS/Add";
|
||||||
|
import { EDITOR_TABS_HEIGHT } from "../IDE/EditorPane/constants";
|
||||||
|
|
||||||
const JSAddState = () => {
|
const JSAddState = () => {
|
||||||
return (
|
return (
|
||||||
<Flex height="100%" justifyContent="center">
|
<Flex height={`calc(100% - ${EDITOR_TABS_HEIGHT})`} justifyContent="center">
|
||||||
<AddJS
|
<AddJS
|
||||||
containerProps={{
|
containerProps={{
|
||||||
px: "spaces-4",
|
px: "spaces-4",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Flex } from "design-system";
|
import { Flex } from "design-system";
|
||||||
import AddQuery from "pages/Editor/IDE/EditorPane/Query/Add";
|
import AddQuery from "pages/Editor/IDE/EditorPane/Query/Add";
|
||||||
|
import { EDITOR_TABS_HEIGHT } from "../IDE/EditorPane/constants";
|
||||||
|
|
||||||
const QueriesAddState = () => {
|
const QueriesAddState = () => {
|
||||||
return (
|
return (
|
||||||
<Flex height="100%" justifyContent="center">
|
<Flex height={`calc(100% - ${EDITOR_TABS_HEIGHT})`} justifyContent="center">
|
||||||
<AddQuery
|
<AddQuery
|
||||||
containerProps={{
|
containerProps={{
|
||||||
px: "spaces-4",
|
px: "spaces-4",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user