Skip to content

Commit

Permalink
chore: entity tabs replacement (appsmithorg#38989)
Browse files Browse the repository at this point in the history
## Description
Replacing entity tab bar and components with ADS templates.


Fixes appsmithorg#37647
Fixes appsmithorg#37775

## 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/13172995565>
> Commit: f3db2d9
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=13172995565&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Thu, 06 Feb 2025 07:56:59 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

## Summary by CodeRabbit

- **New Features**
- Enhanced tab interaction: The add button now supports dynamic
visibility and a loading indicator when new tabs are being created.
- Improved editing: Tab labels now feature refined controls for entering
and exiting edit mode, offering a more flexible user experience.
	- New `EntityTabsHeader` component added to enhance tab management.
- Updated tab structure to utilize `DismissibleTab` for improved
functionality.
- Introduced new props for `DismissibleTabBar` to allow for additional
styling and control over add button visibility.
- New properties added to `DismissibleTab` for greater interactivity and
customization.
- Additional properties introduced to `EditableDismissibleTab` for
enhanced editing capabilities.

- **Refactor**
- Streamlined the tab interface by replacing legacy components with
modern, responsive alternatives, simplifying the overall design and
interaction.
- Enhanced event handling and state management for better performance
and user experience.
- Updated import paths and component structures for clarity and
maintainability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
alex-golovanov and coderabbitai[bot] authored Feb 6, 2025
1 parent 583973b commit bdc33fe
Show file tree
Hide file tree
Showing 40 changed files with 295 additions and 494 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe("Focus Retention of Inputs", { tags: ["@tag.IDE"] }, function () {
cy.setQueryTimeout(10000);

EditorNavigation.SelectEntityByName("SQL_Query", EntityType.Query);
cy.get(locators._queryName).should("contain.text", "SQL_Query");
cy.get(locators._activeEntityTab).should("contain.text", "SQL_Query");
pluginActionForm.toolbar.toggleSettings();
agHelper.GetElement(dataSources._usePreparedStatement).should("be.focused");
EditorNavigation.SelectEntityByName("S3_Query", EntityType.Query);
Expand Down
12 changes: 6 additions & 6 deletions cypress/e2e/Regression/ClientSide/IDE/Tabs_Navigation_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ describe("Tabs Navigation", { tags: ["@tag.IDE"] }, () => {
agHelper.GetNClick(editorTabSelector("page1_query1"));

agHelper
.GetElement(locators._queryName)
.GetElement(locators._activeEntityTab)
.should("have.text", "Page1_Query1");

agHelper.GetNClick(editorTabSelector("page1_query2"));

agHelper
.GetElement(locators._queryName)
.GetElement(locators._activeEntityTab)
.should("have.text", "Page1_Query2");
});

Expand Down Expand Up @@ -93,13 +93,13 @@ describe("Tabs Navigation", { tags: ["@tag.IDE"] }, () => {
agHelper.GetNClick(editorTabSelector("page2_query1"));

agHelper
.GetElement(locators._queryName)
.GetElement(locators._activeEntityTab)
.should("have.text", "Page2_Query1");

agHelper.GetNClick(editorTabSelector("page2_query2"));

agHelper
.GetElement(locators._queryName)
.GetElement(locators._activeEntityTab)
.should("have.text", "Page2_Query2");
});

Expand All @@ -108,13 +108,13 @@ describe("Tabs Navigation", { tags: ["@tag.IDE"] }, () => {
agHelper.GetNClick(editorTabSelector("page1_query1"));

agHelper
.GetElement(locators._queryName)
.GetElement(locators._activeEntityTab)
.should("have.text", "Page1_Query1");

agHelper.GetNClick(editorTabSelector("page1_query2"));

agHelper
.GetElement(locators._queryName)
.GetElement(locators._activeEntityTab)
.should("have.text", "Page1_Query2");

PageLeftPane.switchSegment(PagePaneSegment.JS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe(
jsEditor.RenameJSObjectFromContextMenu("ChangedName1");

// Validate the new name of the JS Object
cy.get(jsEditor.listOfJsObjects).eq(0).contains("ChangedName1");
cy.get(jsEditor.listOfJsDismissibleTabs).eq(0).contains("ChangedName1");

// Create second JS file
jsEditor.CreateJSObject("", { prettify: false, toRun: false });
Expand All @@ -24,7 +24,7 @@ describe(
jsEditor.RenameJSObjectFromContextMenu("ChangedName3");

// Validate the new name of the 3rd JS Objcte
cy.get(jsEditor.listOfJsObjects).eq(2).contains("ChangedName3");
cy.get(jsEditor.listOfJsDismissibleTabs).eq(2).contains("ChangedName3");
});
},
);
4 changes: 2 additions & 2 deletions cypress/e2e/Regression/ServerSide/Datasources/Oracle_Spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ describe(
subAction: "Page1",
toastToValidate: "copied to page",
});
agHelper.GetNAssertContains(locators._queryName, "Query1Copy");
agHelper.GetNAssertContains(locators._activeEntityTab, "Query1Copy");
dataSources.runQueryAndVerifyResponseViews({ count: 2 });
PageList.AddNewPage();
EditorNavigation.SelectEntityByName("Page1", EntityType.Page);
Expand All @@ -456,7 +456,7 @@ describe(
toastToValidate: "moved to page",
});
agHelper.WaitUntilAllToastsDisappear();
agHelper.GetNAssertContains(locators._queryName, "Query1Copy");
agHelper.GetNAssertContains(locators._activeEntityTab, "Query1Copy");
dataSources.runQueryAndVerifyResponseViews({ count: 2 });
agHelper.ActionContextMenuWithInPane({
action: "Delete",
Expand Down
4 changes: 2 additions & 2 deletions cypress/support/ApiCommands.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,9 @@ Cypress.Commands.add("createAndFillApi", (url, parameters) => {
dataSources.NavigateToDSCreateNew();
cy.testCreateApiButton();
cy.get("@createNewApi").then((response) => {
cy.get(locator._queryName).should("be.visible");
cy.get(locator._activeEntityTab).should("be.visible");
expect(response.response.body.responseMeta.success).to.eq(true);
cy.get(locator._queryName)
cy.get(locator._activeEntityTab)
.invoke("text")
.then((text) => {
const someText = text;
Expand Down
4 changes: 2 additions & 2 deletions cypress/support/Objects/CommonLocators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export class CommonLocators {
_link = ".ads-v2-link";
_btnSpinner = ".ads-v2-spinner";
_sidebar = ".t--sidebar";
_queryName = ".editor-tab.active > .ads-v2-text";
_queryNameTxt = ".editor-tab.active > .ads-v2-text input";
_activeEntityTab = ".editor-tab.active .ads-v2-text";
_activeEntityTabInput = ".editor-tab.active .ads-v2-text input";
_editIcon = ".t--action-name-edit-icon";
_emptyCanvasCta = "[data-testid='canvas-ctas']";
_dsName = ".t--edit-datasource-name span";
Expand Down
6 changes: 3 additions & 3 deletions cypress/support/Pages/AggregateHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ export class AggregateHelper {

public RenameQuery(renameVal: string, willFailError?: string) {
this.rename({
nameLocator: this.locator._queryName,
textInputLocator: this.locator._queryNameTxt,
nameLocator: this.locator._activeEntityTab,
textInputLocator: this.locator._activeEntityTabInput,
renameVal,
dblClick: true,
willFailError,
Expand Down Expand Up @@ -1162,7 +1162,7 @@ export class AggregateHelper {

public GetObjectName() {
//cy.get(this.locator._queryName).invoke("text").then((text) => cy.wrap(text).as("queryName")); or below syntax
return cy.get(this.locator._queryName).invoke("text");
return cy.get(this.locator._activeEntityTab).invoke("text");
}

public GetElementLength(selector: string) {
Expand Down
2 changes: 1 addition & 1 deletion cypress/support/Pages/ApiPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class ApiPage {
public settingsTriggerLocator = "[data-testid='t--js-settings-trigger']";
public splitPaneContextMenuTrigger = ".entity-context-menu";
public moreActionsTrigger = "[data-testid='t--more-action-trigger']";
private apiNameInput = ".editor-tab.active > .ads-v2-text input";
private apiNameInput = this.locator._activeEntityTabInput;
public pageList = ".ads-v2-sub-menu > .ads-v2-menu__menu-item";

CreateApi(
Expand Down
7 changes: 4 additions & 3 deletions cypress/support/Pages/JSEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export class JSEditor {
public settingsTriggerLocator = "[data-testid='t--js-settings-trigger']";
public contextMenuTriggerLocator = "[data-testid='t--more-action-trigger']";
public runFunctionSelectLocator = "[data-testid='t--js-function-run']";
public listOfJsObjects = "[data-testid='t--tabs-container']>div>span";
public listOfJsDismissibleTabs =
"[data-testid='t--tabs-container'] .editor-tab";

public toolbar = new PluginEditorToolbar(
this.runButtonLocator,
Expand All @@ -55,8 +56,8 @@ export class JSEditor {
private _onPageLoadSwitchStatus = (functionName: string) =>
`//div[contains(@class, '${functionName}-on-page-load-setting')]//label/input`;

private _jsObjName = ".editor-tab.active > .ads-v2-text";
public _jsObjTxt = ".editor-tab.active > .ads-v2-text input";
private _jsObjName = this.locator._activeEntityTab;
public _jsObjTxt = this.locator._activeEntityTabInput;
public _newJSobj = "span:contains('New JS object')";
private _bindingsClose = ".t--entity-property-close";
public _propertyList = ".binding";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-console */
import type { Meta, StoryObj } from "@storybook/react";

import { DismissibleTab } from ".";
import { DismissibleTab } from "./DismissibleTab";

const meta: Meta<typeof DismissibleTab> = {
title: "ADS/Components/Dismissible Tab",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled from "styled-components";

import { Button as ADSButton } from "..";
import { Button as ADSButton } from "../Button";

export const Tab = styled.div`
position: relative;
Expand All @@ -9,7 +9,8 @@ export const Tab = styled.div`
justify-content: center;
flex-shrink: 0;
gap: var(--ads-v2-spaces-2);
height: 100%;
min-height: 32px;
max-height: 32px;
font-size: 12px;
color: var(--ads-v2-color-fg);
cursor: pointer;
Expand All @@ -20,8 +21,7 @@ export const Tab = styled.div`
border-right: 1px solid transparent;
border-top: 3px solid transparent;
padding: var(--ads-v2-spaces-3);
padding-top: 6px;
padding: 0 var(--ads-v2-spaces-3) 2px var(--ads-v2-spaces-3);
&.active {
background: var(--ads-v2-colors-control-field-default-bg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useCallback } from "react";

import clsx from "classnames";

import { Icon } from "..";
import { Icon } from "../Icon";

import * as Styled from "./DismissibleTab.styles";
import { DATA_TEST_ID } from "./constants";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import type React from "react";

export interface DismissibleTabProps {
/** The content of the tab. */
children: React.ReactNode;
/** Used for custom styling, necessary for styled-components. */
className?: string;
/** Used for passing data-testid. */
dataTestId?: string;
/** Applies active styling. */
isActive?: boolean;
/** Callback when the tab is clicked. */
onClick: () => void;
/** Callback when tab is closed. */
onClose: (e: React.MouseEvent) => void;
/** Callback when tab is double clicked. */
onDoubleClick?: () => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
import React, { useRef, useState } from "react";
import type { Meta, StoryObj } from "@storybook/react";

import {
DismissibleTab,
DismissibleTabBar,
type DismissibleTabBarProps,
} from ".";
import { DismissibleTab } from "./DismissibleTab";
import { DismissibleTabBar } from "./DismissibleTabBar";
import { type DismissibleTabBarProps } from "./DismissibleTabBar.types";

const meta: Meta<typeof DismissibleTabBar> = {
title: "ADS/Components/Dismissible Tab Bar",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled, { css } from "styled-components";

import { Button } from "..";
import { Button } from "../Button";

export const animatedLeftBorder = (showLeftBorder: boolean) => css`
transition: border-color 0.5s ease;
Expand All @@ -16,10 +16,11 @@ export const Root = styled.div<{
}>`
display: flex;
align-items: center;
overflow: hidden;
overflow-x: hidden;
white-space: nowrap;
position: relative;
height: 32px;
max-height: 32px;
min-height: 32px;
${({ $showLeftBorder }) => animatedLeftBorder($showLeftBorder ?? false)};
`;
Expand All @@ -28,7 +29,6 @@ export const TabsContainer = styled.div`
display: flex;
flex: 1 0 auto;
align-items: center;
gap: var(--ads-v2-spaces-2);
height: 100%;
`;

Expand All @@ -41,11 +41,11 @@ export const PlusButtonContainer = styled.div<{ $showLeftBorder?: boolean }>`
position: sticky;
right: 0;
border: none;
min-width: 32px;
height: 100%;
min-width: 28px;
min-height: 32px;
display: flex;
align-items: center;
justify-content: center;
justify-content: end;
${({ $showLeftBorder }) => animatedLeftBorder($showLeftBorder ?? false)};
`;
Expand Down
40 changes: 27 additions & 13 deletions packages/design-system/ads/src/DismissibleTab/DismissibleTabBar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useEffect, useRef, useState } from "react";
import { noop } from "lodash";
import clsx from "clsx";

import { Spinner } from "../Spinner";
import { ScrollArea } from "../ScrollArea";

import * as Styled from "./DismissibleTabBar.styles";
Expand All @@ -14,13 +16,15 @@ export const SCROLL_AREA_OPTIONS = {
} as const;

const SCROLL_AREA_STYLE = {
height: 34,
top: 1,
height: 32,
};

export const DismissibleTabBar = ({
children,
className,
disableAdd = false,
hideAdd = false,
isAddingNewTab,
onTabAdd,
}: DismissibleTabBarProps) => {
const [isLeftIntersecting, setIsLeftIntersecting] = useState(false);
Expand Down Expand Up @@ -85,7 +89,10 @@ export const DismissibleTabBar = ({
);

return (
<Styled.Root $showLeftBorder={isLeftIntersecting}>
<Styled.Root
$showLeftBorder={isLeftIntersecting}
className={clsx(className)}
>
<ScrollArea
data-testid="t--editor-tabs"
options={SCROLL_AREA_OPTIONS}
Expand All @@ -99,16 +106,23 @@ export const DismissibleTabBar = ({
<Styled.StickySentinel ref={sentinelRightRef} />
</Styled.TabsContainer>
</ScrollArea>
<Styled.PlusButtonContainer $showLeftBorder={isRightIntersecting}>
<Styled.PlusButton
isDisabled={disableAdd}
isIconButton
kind="tertiary"
onClick={handleAdd}
startIcon="add-line"
title="Add new tab"
/>
</Styled.PlusButtonContainer>
{!hideAdd && (
<Styled.PlusButtonContainer $showLeftBorder={isRightIntersecting}>
{isAddingNewTab ? (
<Spinner size="md" />
) : (
<Styled.PlusButton
data-testid="t--ide-tabs-add-button"
isDisabled={disableAdd}
isIconButton
kind="tertiary"
onClick={handleAdd}
startIcon="add-line"
title="Add new tab"
/>
)}
</Styled.PlusButtonContainer>
)}
</Styled.Root>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@ import type React from "react";
import type { DismissibleTabProps } from "./DismissibleTab.types";

export interface DismissibleTabBarProps {
/** The content of the tab bar. */
children:
| React.ReactElement<DismissibleTabProps>
| React.ReactElement<DismissibleTabProps>[];
onTabAdd: () => void;
| React.ReactElement<DismissibleTabProps>[]
| React.ReactNode;
/** Used for custom styling, necessary for styled-components. */
className?: string;
/** Button is visible, but disabled & not clickable. */
disableAdd?: boolean;
/** Hides add button completely. */
hideAdd?: boolean;
/** Will display a loader in place of add button. */
isAddingNewTab?: boolean;
/** Callback tab is added. */
onTabAdd: () => void;
}
Loading

0 comments on commit bdc33fe

Please sign in to comment.