chore: Visual changes for core navigation elements on IDE (#37880)
## Description Updates a few visual elements for better Navigational experience using the Segmented Controls Fixes #37881 ## Automation /ok-to-test tags="@tag.IDE, @tag.Sanity" ### 🔍 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/12409098657> > Commit: a94e072ab76b5a1146f25dd822576c6b01e57c1e > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12409098657&attempt=3" target="_blank">Cypress dashboard</a>. > Tags: `@tag.IDE, @tag.Sanity` > Spec: > <hr>Thu, 19 Dec 2024 12:51:29 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** - Added `testId` property to sidebar buttons for enhanced testing capabilities. - Updated button titles from "Data" to "Datasources" for clarity. - Introduced `BottomButtons` configuration for sidebar buttons based on data source availability. - **Bug Fixes** - Improved visual distinction of selected segments in the Segmented Control. - **Style** - Enhanced styling for sidebar buttons and segments, including hover effects and separators. - **Tests** - Added tests to verify rendering of sidebar buttons with the correct test IDs. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
4a91204e4d
commit
1ade47d31a
|
|
@ -6,11 +6,12 @@ import { LeftPane } from "./IDE/LeftPane";
|
||||||
import PageList from "./PageList";
|
import PageList from "./PageList";
|
||||||
|
|
||||||
export enum AppSidebarButton {
|
export enum AppSidebarButton {
|
||||||
Data = "Data",
|
Data = "Datasources",
|
||||||
Editor = "Editor",
|
Editor = "Editor",
|
||||||
Libraries = "Libraries",
|
Libraries = "Libraries",
|
||||||
Settings = "Settings",
|
Settings = "Settings",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppSidebar = new Sidebar(Object.values(AppSidebarButton));
|
export const AppSidebar = new Sidebar(Object.values(AppSidebarButton));
|
||||||
|
|
||||||
export enum PagePaneSegment {
|
export enum PagePaneSegment {
|
||||||
|
|
@ -42,6 +43,7 @@ export enum EntityType {
|
||||||
JSObject = "JSObject",
|
JSObject = "JSObject",
|
||||||
Page = "Page",
|
Page = "Page",
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditorNavigation {
|
class EditorNavigation {
|
||||||
public locators = {
|
public locators = {
|
||||||
MaximizeBtn: "[data-testid='t--ide-maximize']",
|
MaximizeBtn: "[data-testid='t--ide-maximize']",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ export class Sidebar {
|
||||||
buttons: string[];
|
buttons: string[];
|
||||||
locators = {
|
locators = {
|
||||||
sidebar: ".t--sidebar",
|
sidebar: ".t--sidebar",
|
||||||
sidebarButton: (name: string) => `.t--sidebar-${name}`,
|
sidebarButton: (name: string) => `[data-testid='t--sidebar-${name}']`,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(buttons: string[]) {
|
constructor(buttons: string[]) {
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,12 @@ export const StyledSegment = styled.span`
|
||||||
& > * {
|
& > * {
|
||||||
color: var(--ads-v2-colors-control-segment-value-default-fg);
|
color: var(--ads-v2-colors-control-segment-value-default-fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&[data-selected="true"] {
|
||||||
|
span {
|
||||||
|
font-weight: var(--ads-v2-font-weight-bold);
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledControlContainer = styled.div`
|
export const StyledControlContainer = styled.div`
|
||||||
|
|
@ -81,6 +87,7 @@ export const StyledControlContainer = styled.div`
|
||||||
|
|
||||||
/* Select all segments which is not a selected and last child */
|
/* Select all segments which is not a selected and last child */
|
||||||
/* seperator */
|
/* seperator */
|
||||||
|
|
||||||
&:not(:hover):not(:last-child):not([data-selected="true"]):not(
|
&:not(:hover):not(:last-child):not([data-selected="true"]):not(
|
||||||
:has(+ [data-selected="true"])
|
:has(+ [data-selected="true"])
|
||||||
):after {
|
):after {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
<svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M7.16667 12V5.33333H12.5V12H7.16667ZM0.5 6.66667V0H5.83333V6.66667H0.5ZM4.5 5.33333V1.33333H1.83333V5.33333H4.5ZM0.5 12V8H5.83333V12H0.5ZM1.83333 10.6667H4.5V9.33333H1.83333V10.6667ZM8.5 10.6667H11.1667V6.66667H8.5V10.6667ZM7.16667 0H12.5V4H7.16667V0ZM8.5 1.33333V2.66667H11.1667V1.33333H8.5Z" fill="#4C5664"/>
|
<path
|
||||||
|
d="M7.67353 13.8542V7.25693H12.3958V13.8542H7.67353ZM1.77075 8.57638V1.97916H6.49297V8.57638H1.77075ZM5.31242 7.25693V3.2986H2.95131V7.25693H5.31242ZM1.77075 13.8542V9.89582H6.49297V13.8542H1.77075ZM2.95131 12.5347H5.31242V11.2153H2.95131V12.5347ZM8.85409 12.5347H11.2152V8.57638H8.85409V12.5347ZM7.67353 1.97916H12.3958V5.93749H7.67353V1.97916ZM8.85409 3.2986V4.61805H11.2152V3.2986H8.85409Z"
|
||||||
|
fill="currentColor"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 423 B After Width: | Height: | Size: 536 B |
|
|
@ -44,6 +44,7 @@ function IDESidebar(props: IDESidebarProps) {
|
||||||
key={button.state}
|
key={button.state}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
selected={editorState === button.state}
|
selected={editorState === button.state}
|
||||||
|
testId={button.testId}
|
||||||
title={button.title}
|
title={button.title}
|
||||||
tooltip={button.tooltip}
|
tooltip={button.tooltip}
|
||||||
urlSuffix={button.urlSuffix}
|
urlSuffix={button.urlSuffix}
|
||||||
|
|
@ -58,6 +59,7 @@ function IDESidebar(props: IDESidebarProps) {
|
||||||
key={button.state}
|
key={button.state}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
selected={editorState === button.state}
|
selected={editorState === button.state}
|
||||||
|
testId={button.testId}
|
||||||
title={button.title}
|
title={button.title}
|
||||||
tooltip={button.tooltip}
|
tooltip={button.tooltip}
|
||||||
urlSuffix={button.urlSuffix}
|
urlSuffix={button.urlSuffix}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,16 @@ const sidebarButtonProps: SidebarButtonProps = {
|
||||||
selected: false,
|
selected: false,
|
||||||
title: "Test",
|
title: "Test",
|
||||||
urlSuffix: "/test",
|
urlSuffix: "/test",
|
||||||
|
testId: "testId",
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("SidebarButton", () => {
|
describe("SidebarButton", () => {
|
||||||
|
it("should render the button with the correct test id", () => {
|
||||||
|
const { getByTestId } = render(<SidebarButton {...sidebarButtonProps} />);
|
||||||
|
|
||||||
|
expect(getByTestId("t--sidebar-testId")).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
it("should render the warning icon in case the datasource list is empty", () => {
|
it("should render the warning icon in case the datasource list is empty", () => {
|
||||||
const withWarningCondition = {
|
const withWarningCondition = {
|
||||||
...sidebarButtonProps,
|
...sidebarButtonProps,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ const ConditionConfig: Record<Condition, { icon: string; color: string }> = {
|
||||||
|
|
||||||
export interface SidebarButtonProps {
|
export interface SidebarButtonProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
testId: string;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
icon: string;
|
icon: string;
|
||||||
onClick: (urlSuffix: string) => void;
|
onClick: (urlSuffix: string) => void;
|
||||||
|
|
@ -33,10 +34,8 @@ const Container = styled(Flex)`
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const IconContainer = styled.div<{ selected: boolean }>`
|
const IconContainer = styled.div`
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
background-color: ${(props) =>
|
|
||||||
props.selected ? "var(--colors-raw-orange-100, #fbe6dc)" : "white"};
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
|
@ -46,11 +45,16 @@ const IconContainer = styled.div<{ selected: boolean }>`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
&[data-selected="false"] {
|
||||||
|
background-color: var(--ads-v2-color-bg);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: ${(props) =>
|
background-color: var(--ads-v2-color-bg-subtle, #f1f5f9);
|
||||||
props.selected
|
}
|
||||||
? "var(--colors-raw-orange-100, #fbe6dc)"
|
}
|
||||||
: "var(--ads-v2-color-bg-subtle, #f1f5f9);"};
|
|
||||||
|
&[data-selected="true"] {
|
||||||
|
background-color: var(--ads-v2-color-bg-muted);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
@ -85,9 +89,9 @@ function SidebarButton(props: SidebarButtonProps) {
|
||||||
<IconContainer
|
<IconContainer
|
||||||
className={`t--sidebar-${title || tooltip}`}
|
className={`t--sidebar-${title || tooltip}`}
|
||||||
data-selected={selected}
|
data-selected={selected}
|
||||||
|
data-testid={"t--sidebar-" + props.testId}
|
||||||
onClick={handleOnClick}
|
onClick={handleOnClick}
|
||||||
role="button"
|
role="button"
|
||||||
selected={selected}
|
|
||||||
>
|
>
|
||||||
<Icon name={icon} size="lg" />
|
<Icon name={icon} size="lg" />
|
||||||
{condition && (
|
{condition && (
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,11 @@ export enum EditorState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SidebarTopButtonTitles = {
|
export const SidebarTopButtonTitles = {
|
||||||
DATA: "Data",
|
|
||||||
EDITOR: "Editor",
|
EDITOR: "Editor",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SidebarBottomButtonTitles = {
|
export const SidebarBottomButtonTitles = {
|
||||||
|
DATA: "Datasources",
|
||||||
SETTINGS: "Settings",
|
SETTINGS: "Settings",
|
||||||
LIBRARIES: "Libraries",
|
LIBRARIES: "Libraries",
|
||||||
};
|
};
|
||||||
|
|
@ -62,27 +62,31 @@ export const TopButtons: IDESidebarButton[] = [
|
||||||
state: EditorState.EDITOR,
|
state: EditorState.EDITOR,
|
||||||
icon: "editor-v3",
|
icon: "editor-v3",
|
||||||
title: SidebarTopButtonTitles.EDITOR,
|
title: SidebarTopButtonTitles.EDITOR,
|
||||||
|
testId: SidebarTopButtonTitles.EDITOR,
|
||||||
urlSuffix: "",
|
urlSuffix: "",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
state: EditorState.DATA,
|
|
||||||
icon: "datasource-v3",
|
|
||||||
title: SidebarTopButtonTitles.DATA,
|
|
||||||
urlSuffix: "datasource",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const BottomButtons: IDESidebarButton[] = [
|
export const BottomButtons: IDESidebarButton[] = [
|
||||||
|
{
|
||||||
|
state: EditorState.DATA,
|
||||||
|
icon: "datasource-v3",
|
||||||
|
tooltip: SidebarBottomButtonTitles.DATA,
|
||||||
|
testId: SidebarBottomButtonTitles.DATA,
|
||||||
|
urlSuffix: "datasource",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
state: EditorState.LIBRARIES,
|
state: EditorState.LIBRARIES,
|
||||||
icon: "packages-v3",
|
icon: "packages-v3",
|
||||||
tooltip: SidebarBottomButtonTitles.LIBRARIES,
|
tooltip: SidebarBottomButtonTitles.LIBRARIES,
|
||||||
|
testId: SidebarBottomButtonTitles.LIBRARIES,
|
||||||
urlSuffix: "libraries",
|
urlSuffix: "libraries",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
state: EditorState.SETTINGS,
|
state: EditorState.SETTINGS,
|
||||||
icon: "settings-v3",
|
icon: "settings-v3",
|
||||||
tooltip: SidebarBottomButtonTitles.SETTINGS,
|
tooltip: SidebarBottomButtonTitles.SETTINGS,
|
||||||
|
testId: SidebarBottomButtonTitles.SETTINGS,
|
||||||
urlSuffix: "settings",
|
urlSuffix: "settings",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React, { useMemo } from "react";
|
||||||
import { createMessage, EDITOR_PANE_TEXTS } from "ee/constants/messages";
|
import { createMessage, EDITOR_PANE_TEXTS } from "ee/constants/messages";
|
||||||
import { EditorEntityTab } from "ee/entities/IDE/constants";
|
import { EditorEntityTab } from "ee/entities/IDE/constants";
|
||||||
import { useCurrentEditorState, useSegmentNavigation } from "../../hooks";
|
import { useCurrentEditorState, useSegmentNavigation } from "../../hooks";
|
||||||
|
|
@ -8,23 +8,30 @@ const SegmentSwitcher = () => {
|
||||||
const { segment } = useCurrentEditorState();
|
const { segment } = useCurrentEditorState();
|
||||||
const { onSegmentChange } = useSegmentNavigation();
|
const { onSegmentChange } = useSegmentNavigation();
|
||||||
|
|
||||||
return (
|
const segmentOptions = useMemo(() => {
|
||||||
<EditorSegments
|
return [
|
||||||
onSegmentChange={onSegmentChange}
|
|
||||||
options={[
|
|
||||||
{
|
{
|
||||||
label: createMessage(EDITOR_PANE_TEXTS.queries_tab),
|
label: createMessage(EDITOR_PANE_TEXTS.queries_tab),
|
||||||
|
startIcon: "queries-line",
|
||||||
value: EditorEntityTab.QUERIES,
|
value: EditorEntityTab.QUERIES,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: createMessage(EDITOR_PANE_TEXTS.js_tab),
|
label: createMessage(EDITOR_PANE_TEXTS.js_tab),
|
||||||
|
startIcon: "content-type-json",
|
||||||
value: EditorEntityTab.JS,
|
value: EditorEntityTab.JS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: createMessage(EDITOR_PANE_TEXTS.ui_tab),
|
label: createMessage(EDITOR_PANE_TEXTS.ui_tab),
|
||||||
|
startIcon: "dashboard-line",
|
||||||
value: EditorEntityTab.UI,
|
value: EditorEntityTab.UI,
|
||||||
},
|
},
|
||||||
]}
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditorSegments
|
||||||
|
onSegmentChange={onSegmentChange}
|
||||||
|
options={segmentOptions}
|
||||||
selectedSegment={segment}
|
selectedSegment={segment}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ function Sidebar() {
|
||||||
const datasources = useSelector(getDatasources);
|
const datasources = useSelector(getDatasources);
|
||||||
const datasourcesExist = datasources.length > 0;
|
const datasourcesExist = datasources.length > 0;
|
||||||
|
|
||||||
// Updates the top button config based on datasource existence
|
// Updates the bottom button config based on datasource existence
|
||||||
const topButtons = React.useMemo(() => {
|
const bottomButtons = React.useMemo(() => {
|
||||||
return datasourcesExist
|
return datasourcesExist
|
||||||
? TopButtons
|
? BottomButtons
|
||||||
: TopButtons.map((button) => {
|
: BottomButtons.map((button) => {
|
||||||
if (button.state === EditorState.DATA) {
|
if (button.state === EditorState.DATA) {
|
||||||
return {
|
return {
|
||||||
...button,
|
...button,
|
||||||
|
|
@ -64,11 +64,11 @@ function Sidebar() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IDESidebar
|
<IDESidebar
|
||||||
bottomButtons={BottomButtons}
|
bottomButtons={bottomButtons}
|
||||||
editorState={appState}
|
editorState={appState}
|
||||||
id={"t--app-sidebar"}
|
id={"t--app-sidebar"}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
topButtons={topButtons}
|
topButtons={TopButtons}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user