chore: Split library side pane for adding package control section (#36926)

## Description
Purpose of this PR is to split the Libraries section to extend it in EE
and introduced a tab layout that has both js libraries and packages
Changes:
1. Increased width of Libraries pane to 384px
2. Added a route `/package` which opens the same as library 
3. Extracted the JSLibraries section to be reused as a tab panel body in
EE
4. Created `SidePaneWrapper` common component to be reused in EE
5. Split the title of the Libraries section under a hook to return empty
string if package feature flag is enabled.
6. Minor linting refactors.

In CE there is only one visual change i.e the increase in width of the
Libraries side pane from 250px to 384px


[Figma](https://www.figma.com/design/Z0QsSdGOydURn6WIMQ3rHM/Appsmith-Reusability?node-id=6816-319511&node-type=frame&t=x0DzN9HIxjefe3J1-0)

PR for https://github.com/appsmithorg/appsmith-ee/pull/5362
## 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/11434291701>
> Commit: ec76cc569ea90ba55e451f5ab18fa85244ac076f
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11434291701&attempt=3"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Mon, 21 Oct 2024 08:54:31 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

- **New Features**
- Introduced new functions for constructing URLs for application
libraries and packages.
- Added new route constants and matching functions for improved routing
of application packages and libraries.
- Implemented a custom hook for dynamic header titles in the libraries
section.
- Added a new `LibrarySidePane` component to enhance the editor
interface.
- Introduced a new `SidePaneWrapper` component for better layout
management.

- **Bug Fixes**
- Enhanced entity identification logic to recognize both libraries and
packages.

- **Chores**
  - Updated routing logic for improved maintainability.
  - Added constants for configurable UI component widths.

These updates aim to enhance user experience and improve the overall
functionality of the application.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ashit Rath 2024-10-22 10:48:03 +05:30 committed by GitHub
parent 1f72d758c0
commit 67046f8693
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 145 additions and 43 deletions

View File

@ -0,0 +1,29 @@
import { Flex } from "@appsmith/ads";
import React from "react";
import type { ReactNode } from "react";
import styled from "styled-components";
interface SidePaneContainerProps {
children?: ReactNode;
padded?: boolean;
}
const StyledContainer = styled(Flex)<Pick<SidePaneContainerProps, "padded">>`
padding: ${({ padded }) => padded && "var(--ads-v2-spaces-2)"};
`;
function SidePaneWrapper({ children, padded = false }: SidePaneContainerProps) {
return (
<StyledContainer
borderRight="1px solid var(--ads-v2-color-border)"
flexDirection="column"
height="100%"
padded={padded}
width={"100%"}
>
{children}
</StyledContainer>
);
}
export default SidePaneWrapper;

View File

@ -54,6 +54,11 @@ export { default as IDEBottomView } from "./Components/BottomView";
* It switches between different editor states
*/
export { default as IDESidebar } from "./Components/Sidebar";
/**
* IDESidePaneWrapper is used as a wrapper for side panes, which provides a border and optional padding
* and enforces 100% width and height to the parent.
*/
export { default as IDESidePaneWrapper } from "./Components/SidePaneWrapper";
/**
* ToolbarSettingsPopover is a popover attached to a settings toggle button in the toolbar

View File

@ -204,3 +204,12 @@ export const queryAddURL = (props: URLBuilderParams): string =>
...props,
suffix: `queries/add`,
});
export const appLibrariesURL = (): string =>
urlBuilder.build({
suffix: "libraries",
});
export const appPackagesURL = (): string =>
urlBuilder.build({
suffix: "packages",
});

View File

@ -67,6 +67,7 @@ export const JS_COLLECTION_ID_ADD_PATH = `${JS_COLLECTION_EDITOR_PATH}/:baseColl
export const DATA_SOURCES_EDITOR_LIST_PATH = `/datasource`;
export const DATA_SOURCES_EDITOR_ID_PATH = `/datasource/:datasourceId`;
export const APP_LIBRARIES_EDITOR_PATH = `/libraries`;
export const APP_PACKAGES_EDITOR_PATH = `/packages`;
export const APP_SETTINGS_EDITOR_PATH = `/settings`;
export const SAAS_GSHEET_EDITOR_ID_PATH = `/saas/google-sheets-plugin/datasources/:datasourceId`;
export const GEN_TEMPLATE_URL = "generate-page";
@ -128,6 +129,12 @@ export const matchGeneratePagePath = (pathName: string) =>
match(`${BUILDER_CUSTOM_PATH}${GENERATE_TEMPLATE_FORM_PATH}`)(pathName) ||
match(`${BUILDER_PATH_DEPRECATED}${GENERATE_TEMPLATE_FORM_PATH}`)(pathName);
export const matchAppLibrariesPath = (pathName: string) =>
match(`${BUILDER_PATH}${APP_LIBRARIES_EDITOR_PATH}`)(pathName);
export const matchAppPackagesPath = (pathName: string) =>
match(`${BUILDER_PATH}${APP_PACKAGES_EDITOR_PATH}`)(pathName);
export const addBranchParam = (branch: string) => {
const url = new URL(window.location.href);

View File

@ -30,6 +30,7 @@ export const getPageDependencyActions = (
currentWorkspaceId: string = "",
featureFlags: DependentFeatureFlags = {},
allResponses: EditConsolidatedApi,
applicationId: string,
) => {
const { datasources, pagesWithMigratedDsl, plugins } = allResponses || {};
const initActions = [

View File

@ -0,0 +1,12 @@
import { createMessage, HEADER_TITLES } from "ee/constants/messages";
/**
* In CE this returns a simple text as title but this
* hook is extended in EE where based on feature flags
* the title changes
*/
function useLibraryHeaderTitle() {
return createMessage(HEADER_TITLES.LIBRARIES);
}
export default useLibraryHeaderTitle;

View File

@ -0,0 +1,13 @@
import React from "react";
import JSLibrariesSection from "pages/Editor/IDE/LeftPane/JSLibrariesSection";
import { IDESidePaneWrapper } from "IDE";
const LibrarySidePane = () => {
return (
<IDESidePaneWrapper>
<JSLibrariesSection />
</IDESidePaneWrapper>
);
};
export default LibrarySidePane;

View File

@ -4,6 +4,7 @@ import {
API_EDITOR_ID_ADD_PATH,
API_EDITOR_ID_PATH,
APP_LIBRARIES_EDITOR_PATH,
APP_PACKAGES_EDITOR_PATH,
APP_SETTINGS_EDITOR_PATH,
BUILDER_CHECKLIST_PATH,
BUILDER_CUSTOM_PATH,
@ -82,6 +83,7 @@ function useRoutes(path: string): RouteReturnType[] {
`${path}${SAAS_EDITOR_API_ID_PATH}`,
`${path}${SAAS_EDITOR_API_ID_ADD_PATH}`,
`${path}${APP_LIBRARIES_EDITOR_PATH}`,
`${path}${APP_PACKAGES_EDITOR_PATH}`,
`${path}${APP_SETTINGS_EDITOR_PATH}`,
],
},

View File

@ -9,6 +9,7 @@ export const CANVAS_DEFAULT_MIN_ROWS = Math.ceil(
export const DEFAULT_ENTITY_EXPLORER_WIDTH = 256;
export const DEFAULT_PROPERTY_PANE_WIDTH = 288;
export const APP_SETTINGS_PANE_WIDTH = 525;
export const APP_LIBRARIES_PANE_WIDTH = 384;
export const DEFAULT_EXPLORER_PANE_WIDTH = 255;
export const SPLIT_SCREEN_RATIO = 0.404;

View File

@ -0,0 +1,3 @@
export * from "ce/pages/Editor/IDE/Header/useLibraryHeaderTitle";
import { default as CE_useLibraryHeaderTitle } from "ce/pages/Editor/IDE/Header/useLibraryHeaderTitle";
export default CE_useLibraryHeaderTitle;

View File

@ -0,0 +1,3 @@
export * from "ce/pages/Editor/IDE/LeftPane/LibrarySidePane";
import { default as CE_LibrarySidePane } from "ce/pages/Editor/IDE/LeftPane/LibrarySidePane";
export default CE_LibrarySidePane;

View File

@ -199,6 +199,7 @@ export default class AppEditorEngine extends AppEngine {
private *loadPluginsAndDatasources(
allResponses: EditConsolidatedApi,
rootSpan: Span,
applicationId: string,
) {
const loadPluginsAndDatasourcesSpan = startNestedSpan(
"AppEditorEngine.loadPluginsAndDatasources",
@ -211,7 +212,12 @@ export default class AppEditorEngine extends AppEngine {
getFeatureFlagsForEngine,
);
const { errorActions, initActions, successActions } =
getPageDependencyActions(currentWorkspaceId, featureFlags, allResponses);
getPageDependencyActions(
currentWorkspaceId,
featureFlags,
allResponses,
applicationId,
);
if (!isAirgappedInstance) {
initActions.push(fetchMockDatasources(mockDatasources));
@ -259,7 +265,12 @@ export default class AppEditorEngine extends AppEngine {
allResponses,
rootSpan,
);
yield call(this.loadPluginsAndDatasources, allResponses, rootSpan);
yield call(
this.loadPluginsAndDatasources,
allResponses,
rootSpan,
applicationId,
);
}
public *completeChore(rootSpan: Span) {

View File

@ -278,7 +278,10 @@ export function identifyEntityFromPath(path: string): FocusEntityInfo {
}
if (match.params.entity) {
if (match.params.entity === "libraries") {
if (
match.params.entity === "libraries" ||
match.params.entity === "packages"
) {
return {
entity: FocusEntity.LIBRARY,
id: "",

View File

@ -77,6 +77,7 @@ import type { Page } from "entities/Page";
import { IDEHeader, IDEHeaderTitle } from "IDE";
import { APPLICATIONS_URL } from "constants/routes";
import { useNavigationMenuData } from "../../EditorName/useNavigationMenuData";
import useLibraryHeaderTitle from "ee/pages/Editor/IDE/Header/useLibraryHeaderTitle";
const StyledDivider = styled(Divider)`
height: 50%;
@ -92,6 +93,8 @@ interface HeaderTitleProps {
}
const HeaderTitleComponent = ({ appState, currentPage }: HeaderTitleProps) => {
const libraryHeaderTitle = useLibraryHeaderTitle();
switch (appState) {
case EditorState.DATA:
return (
@ -110,12 +113,7 @@ const HeaderTitleComponent = ({ appState, currentPage }: HeaderTitleProps) => {
/>
);
case EditorState.LIBRARIES:
return (
<IDEHeaderTitle
key={appState}
title={createMessage(HEADER_TITLES.LIBRARIES)}
/>
);
return <IDEHeaderTitle key={appState} title={libraryHeaderTitle} />;
default:
return <EditorTitle key={appState} title={currentPage?.pageName || ""} />;
}

View File

@ -16,6 +16,7 @@ import {
import {
APP_SETTINGS_PANE_WIDTH,
APP_SIDEBAR_WIDTH,
APP_LIBRARIES_PANE_WIDTH,
} from "constants/AppConstants";
import { useEditorStateLeftPaneWidth } from "./useEditorStateLeftPaneWidth";
import { type Area, Areas, SIDEBAR_WIDTH } from "../constants";
@ -97,10 +98,10 @@ function useGridLayoutTemplate(): ReturnValue {
} else {
setColumns([
SIDEBAR_WIDTH,
"255px",
`${APP_LIBRARIES_PANE_WIDTH}px`,
(windowWidth -
APP_SIDEBAR_WIDTH -
255 +
APP_LIBRARIES_PANE_WIDTH +
"px") as AnimatedGridUnit,
"0px",
]);

View File

@ -1,13 +1,13 @@
import React from "react";
import AddLibraryPopover from "./AddLibraryPopover";
import PaneHeader from "./PaneHeader";
import { useSelector } from "react-redux";
import React, { useMemo } from "react";
import PaneHeader from "pages/Editor/IDE/LeftPane/PaneHeader";
import AddLibraryPopover from "pages/Editor/IDE/LeftPane/AddLibraryPopover";
import { selectLibrariesForExplorer } from "ee/selectors/entitiesSelector";
import { useSelector } from "react-redux";
import { animated, useTransition } from "react-spring";
import { LibraryEntity } from "pages/Editor/Explorer/Libraries";
import { Flex } from "@appsmith/ads";
const LibrarySidePane = () => {
function JSLibrariesSection() {
const libraries = useSelector(selectLibrariesForExplorer);
const transitions = useTransition(libraries, {
keys: (lib) => lib.name,
@ -16,24 +16,18 @@ const LibrarySidePane = () => {
leave: { opacity: 1 },
});
const rightIcon = useMemo(() => <AddLibraryPopover />, []);
return (
<Flex
borderRight="1px solid var(--ads-v2-color-border)"
flexDirection="column"
height="100%"
width={"100%"}
>
<PaneHeader
rightIcon={<AddLibraryPopover />}
title="Installed Libraries"
/>
<>
<PaneHeader rightIcon={rightIcon} title="Installed Libraries" />
{transitions((style, lib) => (
<animated.div style={style}>
<LibraryEntity lib={lib} />
</animated.div>
))}
</Flex>
</>
);
};
}
export default LibrarySidePane;
export default JSLibrariesSection;

View File

@ -1,9 +1,10 @@
import React from "react";
import React, { useMemo } from "react";
import styled from "styled-components";
import { Switch, useRouteMatch } from "react-router";
import { SentryRoute } from "ee/AppRouter";
import {
APP_LIBRARIES_EDITOR_PATH,
APP_PACKAGES_EDITOR_PATH,
APP_SETTINGS_EDITOR_PATH,
DATA_SOURCES_EDITOR_ID_PATH,
DATA_SOURCES_EDITOR_LIST_PATH,
@ -12,8 +13,8 @@ import {
} from "constants/routes";
import AppSettingsPane from "./AppSettings";
import DataSidePane from "./DataSidePane";
import LibrarySidePane from "./LibrarySidePane";
import EditorPane from "../EditorPane";
import LibrarySidePane from "ee/pages/Editor/IDE/LeftPane/LibrarySidePane";
export const LeftPaneContainer = styled.div<{ showRightBorder?: boolean }>`
height: 100%;
@ -26,23 +27,32 @@ export const LeftPaneContainer = styled.div<{ showRightBorder?: boolean }>`
const LeftPane = () => {
const { path } = useRouteMatch();
const dataSidePanePaths = useMemo(
() => [
`${path}${DATA_SOURCES_EDITOR_LIST_PATH}`,
`${path}${DATA_SOURCES_EDITOR_ID_PATH}`,
`${path}${INTEGRATION_EDITOR_PATH}`,
`${path}${SAAS_GSHEET_EDITOR_ID_PATH}`,
],
[path],
);
const librarySidePanePaths = useMemo(
() => [
`${path}${APP_LIBRARIES_EDITOR_PATH}`,
`${path}${APP_PACKAGES_EDITOR_PATH}`,
],
[path],
);
return (
<LeftPaneContainer showRightBorder={false}>
<Switch>
<SentryRoute
component={DataSidePane}
exact
path={[
`${path}${DATA_SOURCES_EDITOR_LIST_PATH}`,
`${path}${DATA_SOURCES_EDITOR_ID_PATH}`,
`${path}${INTEGRATION_EDITOR_PATH}`,
`${path}${SAAS_GSHEET_EDITOR_ID_PATH}`,
]}
/>
<SentryRoute component={DataSidePane} exact path={dataSidePanePaths} />
<SentryRoute
component={LibrarySidePane}
exact
path={`${path}${APP_LIBRARIES_EDITOR_PATH}`}
path={librarySidePanePaths}
/>
<SentryRoute
component={AppSettingsPane}