Skip to content

Commit

Permalink
feat: Overflow tabs list view (#34150)
Browse files Browse the repository at this point in the history
## Description

This PR implements the new design for the list view.


Fixes #33432  

## Automation

/ok-to-test tags="@tag.Sanity, @tag.IDE"

### 🔍 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/9660135881>
> Commit: fb8addb
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=9660135881&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Sanity, @tag.IDE`

<!-- 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

- **New Features**
  - Introduced `AddTab` component to add new JavaScript or Query tabs.
- Added `ScreenModeToggle` for switching between full-screen and
split-screen modes.
  - Added `FileTab` component for improved tab interactions.
- Introduced `List` component for conditional rendering based on editor
state.

- **Bug Fixes**
- Corrected test assertions and tab names in `JSRender.test.tsx` and
`QueryRender.test.tsx`.
- Fixed tab closure and interaction flow in
`IDE_Add_Pane_Interactions_spec.ts`.

- **Refactor**
- Simplified selector functions and updated component imports for better
readability and performance.

- **Tests**
- Updated tests to include `currentEntity` props and use
`sanitizeString` for tab titles.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
albinAppsmith authored Jun 25, 2024
1 parent d0c2d40 commit 519b53e
Show file tree
Hide file tree
Showing 25 changed files with 492 additions and 376 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe("IDE add pane interactions", { tags: ["@tag.IDE"] }, () => {
// check add pane
PageLeftPane.assertInAddView();
// close add tab
FileTabs.closeTab("new");
FileTabs.closeTab("new_query");
// open add pane to add item
PageLeftPane.switchToAddNew();
// add item
Expand Down
4 changes: 3 additions & 1 deletion app/client/cypress/support/Pages/IDE/FileTabs.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { ObjectsRegistry } from "../../Objects/Registry";
import { sanitizeString } from "../../../../src/utils/URLUtils";
class FileTabs {
locators = {
container: "[data-testid='t--editor-tabs']",
tabName: (name: string) => `[data-testid='t--ide-tab-${name}']`,
tabName: (name: string) =>
`[data-testid='t--ide-tab-${sanitizeString(name)}']`,
tabs: ".editor-tab",
addItem: "[data-testid='t--ide-tabs-add-button']",
closeTab: "[data-testid='t--tab-close-btn']",
Expand Down
98 changes: 98 additions & 0 deletions app/client/src/IDE/Components/FileTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React from "react";
import styled from "styled-components";
import clsx from "classnames";

import { Flex, Icon } from "design-system";
import { sanitizeString } from "utils/URLUtils";

interface FileTabProps {
isActive: boolean;
title: string;
onClick: () => void;
onClose: (e: React.MouseEvent) => void;
icon?: React.ReactNode;
}

export const StyledTab = styled(Flex)`
position: relative;
height: 100%;
font-size: 12px;
color: var(--ads-v2-colors-text-default);
cursor: pointer;
gap: var(--ads-v2-spaces-2);
border-top: 1px solid transparent;
border-top-left-radius: var(--ads-v2-border-radius);
border-top-right-radius: var(--ads-v2-border-radius);
align-items: center;
justify-content: center;
padding: var(--ads-v2-spaces-3);
border-left: 1px solid transparent;
border-right: 1px solid transparent;
border-top: 2px solid transparent;
&.active {
background: var(--ads-v2-colors-control-field-default-bg);
border-top-color: var(--ads-v2-color-bg-brand);
border-left-color: var(--ads-v2-color-border-muted);
border-right-color: var(--ads-v2-color-border-muted);
}
& > .tab-close {
position: relative;
right: -2px;
visibility: hidden;
}
&:hover > .tab-close {
visibility: visible;
}
&.active > .tab-close {
visibility: visible;
}
`;

export const TabTextContainer = styled.span`
width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
`;

export const TabIconContainer = styled.div`
height: 12px;
width: 12px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
img {
width: 12px;
}
`;

export const FileTab = ({
icon,
isActive,
onClick,
onClose,
title,
}: FileTabProps) => {
return (
<StyledTab
className={clsx("editor-tab", isActive && "active")}
data-testid={`t--ide-tab-${sanitizeString(title)}`}
onClick={onClick}
>
{icon ? <TabIconContainer>{icon}</TabIconContainer> : null}
<TabTextContainer>{title}</TabTextContainer>
{/* not using button component because of the size not matching design */}
<Icon
className="tab-close rounded-[4px] hover:bg-[var(--ads-v2-colors-action-tertiary-surface-hover-bg)] cursor-pointer p-[2px]"
data-testid="t--tab-close-btn"
name="close-line"
onClick={onClose}
/>
</StyledTab>
);
};
3 changes: 2 additions & 1 deletion app/client/src/ce/pages/Editor/IDE/EditorPane/JS/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import history from "utils/history";
import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity";
import { useModuleOptions } from "@appsmith/utils/moduleInstanceHelpers";
import { getJSUrl } from "@appsmith/pages/Editor/IDE/EditorPane/JS/utils";
import { JSBlankState } from "pages/Editor/JSEditor/JSBlankState";

export const useJSAdd = () => {
const pageId = useSelector(getCurrentPageId);
Expand Down Expand Up @@ -95,7 +96,7 @@ export const useJSSegmentRoutes = (path: string): UseRoutes => {
},
{
key: "JSEmpty",
component: ListJS,
component: JSBlankState,
exact: true,
path: [path],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { Tag, type ListItemProps } from "design-system";
import { useCurrentEditorState } from "pages/Editor/IDE/hooks";
import CurlImportEditor from "pages/Editor/APIEditor/CurlImportEditor";
import { createAddClassName } from "pages/Editor/IDE/EditorPane/utils";
import { QueriesBlankState } from "pages/Editor/QueryEditor/QueriesBlankState";

export const useQueryAdd = () => {
const location = useLocation();
Expand Down Expand Up @@ -161,7 +162,7 @@ export const useQuerySegmentRoutes = (path: string): UseRoutes => {
},
{
key: "QueryEmpty",
component: ListQuery,
component: QueriesBlankState,
exact: true,
path: [path],
},
Expand Down
43 changes: 19 additions & 24 deletions app/client/src/ce/selectors/appIDESelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
getQuerySegmentItems,
} from "@appsmith/selectors/entitiesSelector";
import { getJSTabs, getQueryTabs } from "selectors/ideSelectors";
import type { AppState } from "@appsmith/reducers";

export type EditorSegmentList = Array<{
group: string | "NA";
Expand Down Expand Up @@ -45,28 +46,22 @@ export const selectJSSegmentEditorList = createSelector(
},
);

export const selectJSSegmentEditorTabs = createSelector(
getJSSegmentItems,
getJSTabs,
(items, tabs) => {
const keyedItems = keyBy(items, "key");
return tabs
.map((tab) => {
return keyedItems[tab];
})
.filter(Boolean);
},
);
export const selectJSSegmentEditorTabs = (state: AppState) => {
const items = getJSSegmentItems(state);
const tabs = getJSTabs(state);

export const selectQuerySegmentEditorTabs = createSelector(
getQuerySegmentItems,
getQueryTabs,
(items, tabs) => {
const keyedItems = keyBy(items, "key");
return tabs
.map((tab) => {
return keyedItems[tab];
})
.filter(Boolean);
},
);
const keyedItems = keyBy(items, "key");
return tabs
.map((tab) => {
return keyedItems[tab];
})
.filter(Boolean);
};

export const selectQuerySegmentEditorTabs = (state: AppState) => {
const items = getQuerySegmentItems(state);
const tabs = getQueryTabs(state);

const keyedItems = keyBy(items, "key");
return tabs.map((tab) => keyedItems[tab]).filter(Boolean);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import QueriesSegment from "./Query";
import WidgetsSegment from "./UI";
import JSSegment from "./JS";
import SegmentedHeader from "./components/SegmentedHeader";
import EditorTabs from "../EditorTabs/SplitScreenTabs";
import EditorTabs from "../EditorTabs";
import {
jsSegmentRoutes,
querySegmentRoutes,
Expand All @@ -17,19 +17,23 @@ import {
BUILDER_PATH,
BUILDER_PATH_DEPRECATED,
} from "@appsmith/constants/routes/appRoutes";
import { useSelector } from "react-redux";
import { getIDEViewMode } from "selectors/ideSelectors";
import { EditorViewMode } from "@appsmith/entities/IDE/constants";

const EditorPaneSegments = () => {
const { path } = useRouteMatch();
const ideViewMode = useSelector(getIDEViewMode);

return (
<Flex
className="relative"
flexDirection="column"
gap="spacing-2"
height="100%"
overflow="hidden"
>
<SegmentedHeader />
<EditorTabs />
{ideViewMode === EditorViewMode.SplitScreen ? <EditorTabs /> : null}
<Flex
className="ide-editor-left-pane__content"
flexDirection="column"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ describe("IDE Render: JS", () => {
).toBe(true);
// Tabs active state
expect(
getByTestId("t--ide-tab-JSObject1").classList.contains("active"),
getByTestId("t--ide-tab-jsobject1").classList.contains("active"),
).toBe(true);
// Check if the form is rendered
expect(container.querySelector(".js-editor-tab")).not.toBeNull();
Expand Down Expand Up @@ -201,7 +201,7 @@ describe("IDE Render: JS", () => {
expect(getAllByText("JSObject2").length).toBe(2);
// Tabs active state
expect(
getByTestId("t--ide-tab-JSObject2").classList.contains("active"),
getByTestId("t--ide-tab-jsobject2").classList.contains("active"),
).toBe(true);

// Check if the form is rendered
Expand Down Expand Up @@ -245,7 +245,7 @@ describe("IDE Render: JS", () => {
expect(getAllByText("JSObject3").length).toEqual(2);
// Tabs active state
expect(
getByTestId("t--ide-tab-JSObject3").classList.contains("active"),
getByTestId("t--ide-tab-jsobject3").classList.contains("active"),
).toBe(false);
// Check js object is not rendered. Instead new tab should render
expect(container.querySelector(".js-editor-tab")).toBeNull();
Expand Down Expand Up @@ -283,7 +283,7 @@ describe("IDE Render: JS", () => {
expect(getAllByText("JSObject4").length).toEqual(1);
// Tabs active state
expect(
getByTestId("t--ide-tab-JSObject4").classList.contains("active"),
getByTestId("t--ide-tab-jsobject4").classList.contains("active"),
).toBe(false);

// Check if the form is not rendered
Expand Down
2 changes: 1 addition & 1 deletion app/client/src/pages/Editor/IDE/EditorPane/JS/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const ListJSObjects = () => {
return (
<JSContainer
className="ide-editor-left-pane__content-js"
flex="1"
flexDirection="column"
gap="spaces-3"
overflow="hidden"
Expand All @@ -77,7 +78,6 @@ const ListJSObjects = () => {
>
<Flex
data-testid="t--ide-list"
flex="1"
flexDirection="column"
gap="spaces-4"
overflowY="auto"
Expand Down
Loading

0 comments on commit 519b53e

Please sign in to comment.