Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: entity tabs replacement #38989

Merged
merged 41 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
27a8d4b
feat: extract segment header into ads
alex-golovanov Jan 15, 2025
c9a9d8a
feat: add a dismissible tab component to ads
alex-golovanov Jan 17, 2025
aca2541
feat: move useEditableText to hooks
alex-golovanov Jan 20, 2025
a2d7849
feat: add editable entity name template
alex-golovanov Jan 20, 2025
777b8c7
feat: revert to icon component as a prop & fix positioning
alex-golovanov Jan 21, 2025
1a43e3f
feat: add editable dismissible tab template
alex-golovanov Jan 21, 2025
6d3e7ea
chore: accessibility tweaks
alex-golovanov Jan 21, 2025
7c4a5be
chore: remove unnecessary callback
alex-golovanov Jan 22, 2025
d299e5b
chore: change all ads imports to relative paths
alex-golovanov Jan 22, 2025
3d320f1
Merge branch 'release' into feat/37649-entity-tab-component
alex-golovanov Jan 22, 2025
2bec5e8
fix: change path to prevent unit test failure
alex-golovanov Jan 22, 2025
2498569
chore: move types to separate files
alex-golovanov Jan 23, 2025
23c8fbe
chore: move types to separate files
alex-golovanov Jan 23, 2025
b676070
Merge branch 'feat/37649-entity-tab-component' into feat/37692-scroll…
alex-golovanov Jan 23, 2025
2e48d37
perf: add optional ref forwarding
alex-golovanov Jan 27, 2025
5f44034
perf: prevent event bubbling on tab close
alex-golovanov Jan 27, 2025
a011e73
feat: add scrollable tab list
alex-golovanov Jan 27, 2025
cf0324e
Merge branch 'release' into feat/37692-scrollable-tabs-list
alex-golovanov Jan 27, 2025
e4cbb72
Update app/client/packages/design-system/ads/src/DismissibleTab/Dismi…
alex-golovanov Jan 27, 2025
a392bc2
Merge branch 'feat/37692-scrollable-tabs-list' into feat/37688-entity…
alex-golovanov Jan 27, 2025
41a8bd7
feat: create entity list button preset
alex-golovanov Jan 28, 2025
4516fdd
Merge branch 'release' into feat/37688-entity-tabs-header
alex-golovanov Jan 29, 2025
68160d9
chore: make isActive optional
alex-golovanov Jan 29, 2025
2380dfd
feat: add button presets & type enforcement
alex-golovanov Jan 29, 2025
7089891
perf: added icon toggle & loosen types for the toggle control
alex-golovanov Jan 30, 2025
0d25bc4
Merge branch 'feat/37688-entity-tabs-header' into chore/37647-entity-…
alex-golovanov Jan 30, 2025
cd32aee
Merge branch 'release' into chore/37647-entity-tabs-replacement
alex-golovanov Jan 31, 2025
3a96744
perf: add optional handlers for renaming
alex-golovanov Feb 3, 2025
9b5b097
perf: add spinner and ability to hide add button
alex-golovanov Feb 3, 2025
cd3f3ae
Remove deprecated EditorTabs components and update tab functionality
alex-golovanov Feb 3, 2025
b7890b0
Merge branch 'release' into chore/37647-entity-tabs-replacement
alex-golovanov Feb 3, 2025
c92bef9
Refactor locators for query and JS object names.
alex-golovanov Feb 4, 2025
4c80891
Restore data-testid attributes & update unit tests.
alex-golovanov Feb 4, 2025
ec7ce0a
Refactor dismissible tabs to protrude when in entity tab bar
alex-golovanov Feb 4, 2025
12442d3
Resolved tabs related circular dependencies
alex-golovanov Feb 4, 2025
b01b66c
Unified tab selectors
alex-golovanov Feb 4, 2025
a2e8813
Refactor DismissibleTab and EditableEntityName interfaces to enhance …
alex-golovanov Feb 5, 2025
9dc1e0c
Refactor AddTab component to use dynamic dataTestId for tab identific…
alex-golovanov Feb 5, 2025
fe1514f
Update tests to check for empty tabs using roles
alex-golovanov Feb 5, 2025
156be69
Adjust positioning of buttons in tab bar to align correctly
alex-golovanov Feb 5, 2025
f3db2d9
chore: set fixed height to dismissible tab
alex-golovanov Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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");
});
},
);
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 app/client/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 app/client/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 app/client/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 @@ -1160,7 +1160,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 app/client/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 app/client/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 @@ -20,8 +20,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
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;
ankitakinger marked this conversation as resolved.
Show resolved Hide resolved
/** Callback tab is added. */
onTabAdd: () => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";

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

const meta: Meta<typeof EditableDismissibleTab> = {
title: "ADS/Templates/Editable Dismissible Tab",
Expand All @@ -24,7 +24,7 @@ export const Basic: Story = {
dataTestId: "t--dismissible-tab",
icon: JSIcon(),
name: "Hello",
canEdit: true,
isEditable: true,

onNameSave: console.log,
validateName: (name: string) =>
Expand Down
Loading
Loading