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:
Hetu Nandu 2024-12-19 18:57:44 +05:30 committed by GitHub
parent 4a91204e4d
commit 1ade47d31a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 77 additions and 41 deletions

View File

@ -6,11 +6,12 @@ import { LeftPane } from "./IDE/LeftPane";
import PageList from "./PageList";
export enum AppSidebarButton {
Data = "Data",
Data = "Datasources",
Editor = "Editor",
Libraries = "Libraries",
Settings = "Settings",
}
export const AppSidebar = new Sidebar(Object.values(AppSidebarButton));
export enum PagePaneSegment {
@ -42,6 +43,7 @@ export enum EntityType {
JSObject = "JSObject",
Page = "Page",
}
class EditorNavigation {
public locators = {
MaximizeBtn: "[data-testid='t--ide-maximize']",

View File

@ -2,7 +2,7 @@ export class Sidebar {
buttons: string[];
locators = {
sidebar: ".t--sidebar",
sidebarButton: (name: string) => `.t--sidebar-${name}`,
sidebarButton: (name: string) => `[data-testid='t--sidebar-${name}']`,
};
constructor(buttons: string[]) {

View File

@ -41,6 +41,12 @@ export const StyledSegment = styled.span`
& > * {
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`
@ -81,6 +87,7 @@ export const StyledControlContainer = styled.div`
/* Select all segments which is not a selected and last child */
/* seperator */
&:not(:hover):not(:last-child):not([data-selected="true"]):not(
:has(+ [data-selected="true"])
):after {

View File

@ -1,3 +1,6 @@
<svg width="13" height="12" viewBox="0 0 13 12" 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"/>
<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<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>

Before

Width:  |  Height:  |  Size: 423 B

After

Width:  |  Height:  |  Size: 536 B

View File

@ -44,6 +44,7 @@ function IDESidebar(props: IDESidebarProps) {
key={button.state}
onClick={onClick}
selected={editorState === button.state}
testId={button.testId}
title={button.title}
tooltip={button.tooltip}
urlSuffix={button.urlSuffix}
@ -58,6 +59,7 @@ function IDESidebar(props: IDESidebarProps) {
key={button.state}
onClick={onClick}
selected={editorState === button.state}
testId={button.testId}
title={button.title}
tooltip={button.tooltip}
urlSuffix={button.urlSuffix}

View File

@ -11,9 +11,16 @@ const sidebarButtonProps: SidebarButtonProps = {
selected: false,
title: "Test",
urlSuffix: "/test",
testId: "testId",
};
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", () => {
const withWarningCondition = {
...sidebarButtonProps,

View File

@ -16,6 +16,7 @@ const ConditionConfig: Record<Condition, { icon: string; color: string }> = {
export interface SidebarButtonProps {
title?: string;
testId: string;
selected: boolean;
icon: string;
onClick: (urlSuffix: string) => void;
@ -33,10 +34,8 @@ const Container = styled(Flex)`
padding: 8px 0;
`;
const IconContainer = styled.div<{ selected: boolean }>`
const IconContainer = styled.div`
padding: 2px;
background-color: ${(props) =>
props.selected ? "var(--colors-raw-orange-100, #fbe6dc)" : "white"};
border-radius: 3px;
width: 32px;
height: 32px;
@ -46,11 +45,16 @@ const IconContainer = styled.div<{ selected: boolean }>`
cursor: pointer;
position: relative;
&:hover {
background: ${(props) =>
props.selected
? "var(--colors-raw-orange-100, #fbe6dc)"
: "var(--ads-v2-color-bg-subtle, #f1f5f9);"};
&[data-selected="false"] {
background-color: var(--ads-v2-color-bg);
&:hover {
background-color: 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
className={`t--sidebar-${title || tooltip}`}
data-selected={selected}
data-testid={"t--sidebar-" + props.testId}
onClick={handleOnClick}
role="button"
selected={selected}
>
<Icon name={icon} size="lg" />
{condition && (

View File

@ -31,11 +31,11 @@ export enum EditorState {
}
export const SidebarTopButtonTitles = {
DATA: "Data",
EDITOR: "Editor",
};
export const SidebarBottomButtonTitles = {
DATA: "Datasources",
SETTINGS: "Settings",
LIBRARIES: "Libraries",
};
@ -62,27 +62,31 @@ export const TopButtons: IDESidebarButton[] = [
state: EditorState.EDITOR,
icon: "editor-v3",
title: SidebarTopButtonTitles.EDITOR,
testId: SidebarTopButtonTitles.EDITOR,
urlSuffix: "",
},
{
state: EditorState.DATA,
icon: "datasource-v3",
title: SidebarTopButtonTitles.DATA,
urlSuffix: "datasource",
},
];
export const BottomButtons: IDESidebarButton[] = [
{
state: EditorState.DATA,
icon: "datasource-v3",
tooltip: SidebarBottomButtonTitles.DATA,
testId: SidebarBottomButtonTitles.DATA,
urlSuffix: "datasource",
},
{
state: EditorState.LIBRARIES,
icon: "packages-v3",
tooltip: SidebarBottomButtonTitles.LIBRARIES,
testId: SidebarBottomButtonTitles.LIBRARIES,
urlSuffix: "libraries",
},
{
state: EditorState.SETTINGS,
icon: "settings-v3",
tooltip: SidebarBottomButtonTitles.SETTINGS,
testId: SidebarBottomButtonTitles.SETTINGS,
urlSuffix: "settings",
},
];

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useMemo } from "react";
import { createMessage, EDITOR_PANE_TEXTS } from "ee/constants/messages";
import { EditorEntityTab } from "ee/entities/IDE/constants";
import { useCurrentEditorState, useSegmentNavigation } from "../../hooks";
@ -8,23 +8,30 @@ const SegmentSwitcher = () => {
const { segment } = useCurrentEditorState();
const { onSegmentChange } = useSegmentNavigation();
const segmentOptions = useMemo(() => {
return [
{
label: createMessage(EDITOR_PANE_TEXTS.queries_tab),
startIcon: "queries-line",
value: EditorEntityTab.QUERIES,
},
{
label: createMessage(EDITOR_PANE_TEXTS.js_tab),
startIcon: "content-type-json",
value: EditorEntityTab.JS,
},
{
label: createMessage(EDITOR_PANE_TEXTS.ui_tab),
startIcon: "dashboard-line",
value: EditorEntityTab.UI,
},
];
}, []);
return (
<EditorSegments
onSegmentChange={onSegmentChange}
options={[
{
label: createMessage(EDITOR_PANE_TEXTS.queries_tab),
value: EditorEntityTab.QUERIES,
},
{
label: createMessage(EDITOR_PANE_TEXTS.js_tab),
value: EditorEntityTab.JS,
},
{
label: createMessage(EDITOR_PANE_TEXTS.ui_tab),
value: EditorEntityTab.UI,
},
]}
options={segmentOptions}
selectedSegment={segment}
/>
);

View File

@ -26,11 +26,11 @@ function Sidebar() {
const datasources = useSelector(getDatasources);
const datasourcesExist = datasources.length > 0;
// Updates the top button config based on datasource existence
const topButtons = React.useMemo(() => {
// Updates the bottom button config based on datasource existence
const bottomButtons = React.useMemo(() => {
return datasourcesExist
? TopButtons
: TopButtons.map((button) => {
? BottomButtons
: BottomButtons.map((button) => {
if (button.state === EditorState.DATA) {
return {
...button,
@ -64,11 +64,11 @@ function Sidebar() {
return (
<IDESidebar
bottomButtons={BottomButtons}
bottomButtons={bottomButtons}
editorState={appState}
id={"t--app-sidebar"}
onClick={onClick}
topButtons={topButtons}
topButtons={TopButtons}
/>
);
}