chore: Moving navigation header to Explorer templates in ADS (#38131)
## Description Moving navigation header to Explorer templates in ADS Fixes [#37614](https://github.com/appsmithorg/appsmith/issues/37614) [#37612](https://github.com/appsmithorg/appsmith/issues/37612) [#37611](https://github.com/appsmithorg/appsmith/issues/37611) ## 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/12380719410> > Commit: 440da6dff557348bd4524127c86ecaeee19dfe41 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12380719410&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.IDE, @tag.Sanity` > Spec: > <hr>Tue, 17 Dec 2024 21:00:53 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 - **New Features** - Introduced a new `SegmentSwitcher` component for enhanced segment navigation. - Added `EditorSegments` component for rendering a segmented control interface. - **Improvements** - Replaced the `SegmentedHeader` with `SegmentSwitcher` in the explorer pane for improved user experience. - Updated the header component to use the new `NavigationHeader` for better segment management. - Enhanced hover interactions and simplified separator logic within the segmented control. - **Exports** - New exports for `EditorSegments` and `SegmentSwitcher` components to facilitate usage across modules. - Added new documentation for the `EditorSegments` component to provide usage guidelines and examples. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
35a5d83ec7
commit
495f139a83
|
|
@ -55,6 +55,10 @@ export const StyledControlContainer = styled.div`
|
|||
cursor: pointer;
|
||||
height: 100%;
|
||||
|
||||
&[data-selected="false"]:hover {
|
||||
background-color: var(--ads-v2-color-bg-muted);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--ads-v2-border-width-outline) solid
|
||||
var(--ads-v2-color-outline);
|
||||
|
|
@ -77,7 +81,7 @@ export const StyledControlContainer = styled.div`
|
|||
|
||||
/* Select all segments which is not a selected and last child */
|
||||
/* seperator */
|
||||
&:not(:last-child):not([data-selected="true"]):not(
|
||||
&:not(:hover):not(:last-child):not([data-selected="true"]):not(
|
||||
:has(+ [data-selected="true"])
|
||||
):after {
|
||||
content: "";
|
||||
|
|
@ -87,15 +91,4 @@ export const StyledControlContainer = styled.div`
|
|||
height: 16px;
|
||||
background-color: var(--ads-v2-colors-control-field-default-border);
|
||||
}
|
||||
|
||||
/* This before is to mask the separator in left side of selected control */
|
||||
/* Mask the seperator with track background color */
|
||||
&[data-selected="true"]:not(:first-child):after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -7px;
|
||||
width: 2px;
|
||||
height: 16px;
|
||||
background-color: var(--ads-v2-colors-control-track-default-bg);
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
import { Canvas, Meta } from "@storybook/blocks";
|
||||
|
||||
import * as EditorSegmentsStories from "./EditorSegments.stories";
|
||||
|
||||
<Meta of={EditorSegmentsStories} />
|
||||
|
||||
# Editor Segments
|
||||
|
||||
Editor Segments is is built on top of the ADS component - Segmented Control. It is a preset template built for Entity Explorer which has a `max-width of 247px` and has extra padding around each of the segments.
|
||||
|
||||
Editor Segments present a range of options, and should be used when the user can execute those options instantaneously. It can also accept `children` to add more UI controls, if needed. These will be placed on the right side of the segments. It takes `options`, `selectedSegment` and `onSegmentChange` props to handle the value and onChange functionalities of Segmented Control component.
|
||||
|
||||
The options presented should not be binary. If you have a `yes | no` scenario, use a switch, instead.
|
||||
|
||||
## Anatomy
|
||||
|
||||
### Default implementation
|
||||
|
||||
Note: The `options` needs to be passed as required, the UI shown below are not default values being provided to the Editor segments. It also means the `selectedSegment` and `onSegmentChange` props needs to be passed as well.
|
||||
|
||||
<Canvas of={EditorSegmentsStories.EditorSegmentsStory} />
|
||||
|
||||
### More UI controls
|
||||
|
||||
<Canvas of={EditorSegmentsStories.EditorSegmentsStoryWithChildren} />
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
import React from "react";
|
||||
import { EditorSegments } from "./EditorSegments";
|
||||
import type { EditorSegmentsProps } from "./EditorSegments.types";
|
||||
import type { StoryObj } from "@storybook/react";
|
||||
import { Button } from "../../../Button";
|
||||
|
||||
export default {
|
||||
title: "ADS/Templates/Entity Explorer/Editor Segments",
|
||||
component: EditorSegments,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
const Template = (args: EditorSegmentsProps) => {
|
||||
return <EditorSegments {...args}>{args.children}</EditorSegments>;
|
||||
};
|
||||
|
||||
export const EditorSegmentsStory = Template.bind({}) as StoryObj;
|
||||
EditorSegmentsStory.storyName = "Default";
|
||||
EditorSegmentsStory.args = {
|
||||
options: [
|
||||
{
|
||||
value: "queries",
|
||||
label: "Queries",
|
||||
startIcon: "queries-v3",
|
||||
},
|
||||
{
|
||||
value: "js",
|
||||
label: "JS",
|
||||
startIcon: "content-type-json",
|
||||
},
|
||||
{
|
||||
value: "ui",
|
||||
label: "UI",
|
||||
startIcon: "dashboard-line",
|
||||
isDisabled: true,
|
||||
},
|
||||
],
|
||||
defaultValue: "queries",
|
||||
};
|
||||
|
||||
export const EditorSegmentsStoryWithIcons = Template.bind({}) as StoryObj;
|
||||
EditorSegmentsStoryWithIcons.storyName = "Only Icons";
|
||||
EditorSegmentsStoryWithIcons.args = {
|
||||
options: [
|
||||
{
|
||||
value: "queries",
|
||||
startIcon: "queries-v3",
|
||||
},
|
||||
{
|
||||
value: "js",
|
||||
startIcon: "content-type-json",
|
||||
},
|
||||
{
|
||||
value: "ui",
|
||||
startIcon: "dashboard-line",
|
||||
},
|
||||
],
|
||||
defaultValue: "queries",
|
||||
};
|
||||
|
||||
export const EditorSegmentsStoryWithLabels = Template.bind({}) as StoryObj;
|
||||
EditorSegmentsStoryWithLabels.storyName = "Only Labels";
|
||||
EditorSegmentsStoryWithLabels.args = {
|
||||
options: [
|
||||
{
|
||||
value: "queries",
|
||||
label: "Queries",
|
||||
},
|
||||
{
|
||||
value: "js",
|
||||
label: "JS",
|
||||
},
|
||||
{
|
||||
value: "ui",
|
||||
label: "UI",
|
||||
},
|
||||
],
|
||||
defaultValue: "queries",
|
||||
};
|
||||
|
||||
export const EditorSegmentsStoryWithChildren = Template.bind({}) as StoryObj;
|
||||
EditorSegmentsStoryWithChildren.storyName = "With Children";
|
||||
EditorSegmentsStoryWithChildren.args = {
|
||||
options: [
|
||||
{
|
||||
value: "queries",
|
||||
label: "Queries",
|
||||
},
|
||||
{
|
||||
value: "js",
|
||||
label: "JS",
|
||||
},
|
||||
{
|
||||
value: "ui",
|
||||
label: "UI",
|
||||
},
|
||||
],
|
||||
defaultValue: "queries",
|
||||
isFullWidth: true,
|
||||
children: <Button isIconButton kind="secondary" startIcon="plus" />,
|
||||
};
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import styled from "styled-components";
|
||||
import { Flex } from "../../../Flex";
|
||||
|
||||
export const Container = styled(Flex)`
|
||||
.editor-pane-segment-control {
|
||||
max-width: 247px;
|
||||
}
|
||||
`;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import React from "react";
|
||||
import { SegmentedControl } from "../../../SegmentedControl";
|
||||
import { Container } from "./EditorSegments.styles";
|
||||
import type { EditorSegmentsProps } from "./EditorSegments.types";
|
||||
|
||||
const EditorSegments = (props: EditorSegmentsProps) => {
|
||||
const { children, onSegmentChange, options, selectedSegment } = props;
|
||||
|
||||
return (
|
||||
<Container
|
||||
alignItems="center"
|
||||
backgroundColor="var(--ads-v2-colors-control-track-default-bg)"
|
||||
className="ide-editor-left-pane__header"
|
||||
gap="spaces-2"
|
||||
justifyContent="space-between"
|
||||
padding="spaces-2"
|
||||
>
|
||||
<SegmentedControl
|
||||
className="editor-pane-segment-control"
|
||||
onChange={onSegmentChange}
|
||||
options={options}
|
||||
value={selectedSegment}
|
||||
/>
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
EditorSegments.displayName = "EditorSegments";
|
||||
|
||||
EditorSegments.defaultProps = {};
|
||||
|
||||
export { EditorSegments };
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import type { SegmentedControlOption } from "../../../SegmentedControl";
|
||||
|
||||
export interface EditorSegmentsProps {
|
||||
selectedSegment: string;
|
||||
onSegmentChange: (value: string) => void;
|
||||
options: SegmentedControlOption[];
|
||||
children?: React.ReactNode | React.ReactNode[];
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export * from "./EditorSegments";
|
||||
export * from "./EditorSegments.types";
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
export { ListItemContainer, ListHeaderContainer } from "./styles";
|
||||
export { ListWithHeader } from "./ListWithHeader";
|
||||
export { EditorSegments } from "./EditorSegments";
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
BUILDER_PATH,
|
||||
BUILDER_PATH_DEPRECATED,
|
||||
} from "ee/constants/routes/appRoutes";
|
||||
import SegmentedHeader from "./components/SegmentedHeader";
|
||||
import SegmentSwitcher from "./components/SegmentSwitcher";
|
||||
import { useSelector } from "react-redux";
|
||||
import { getIDEViewMode } from "selectors/ideSelectors";
|
||||
import { EditorViewMode } from "ee/entities/IDE/constants";
|
||||
|
|
@ -41,7 +41,7 @@ const EditorPaneExplorer = () => {
|
|||
: "100%"
|
||||
}
|
||||
>
|
||||
<SegmentedHeader />
|
||||
<SegmentSwitcher />
|
||||
<Switch>
|
||||
<SentryRoute
|
||||
component={JSExplorer}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import React from "react";
|
||||
import { createMessage, EDITOR_PANE_TEXTS } from "ee/constants/messages";
|
||||
import { EditorEntityTab } from "ee/entities/IDE/constants";
|
||||
import { useCurrentEditorState, useSegmentNavigation } from "../../hooks";
|
||||
import { EditorSegments } from "@appsmith/ads";
|
||||
|
||||
const SegmentSwitcher = () => {
|
||||
const { segment } = useCurrentEditorState();
|
||||
const { onSegmentChange } = useSegmentNavigation();
|
||||
|
||||
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,
|
||||
},
|
||||
]}
|
||||
selectedSegment={segment}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SegmentSwitcher;
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
import React from "react";
|
||||
import { Flex, SegmentedControl } from "@appsmith/ads";
|
||||
import { createMessage, EDITOR_PANE_TEXTS } from "ee/constants/messages";
|
||||
import { EditorEntityTab } from "ee/entities/IDE/constants";
|
||||
import { useCurrentEditorState, useSegmentNavigation } from "../../hooks";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Container = styled(Flex)`
|
||||
#editor-pane-segment-control {
|
||||
max-width: 247px;
|
||||
}
|
||||
`;
|
||||
|
||||
const SegmentedHeader = () => {
|
||||
const { segment } = useCurrentEditorState();
|
||||
const { onSegmentChange } = useSegmentNavigation();
|
||||
|
||||
return (
|
||||
<Container
|
||||
alignItems="center"
|
||||
backgroundColor="var(--ads-v2-colors-control-track-default-bg)"
|
||||
className="ide-editor-left-pane__header"
|
||||
gap="spaces-2"
|
||||
justifyContent="space-between"
|
||||
padding="spaces-2"
|
||||
>
|
||||
<SegmentedControl
|
||||
id="editor-pane-segment-control"
|
||||
onChange={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,
|
||||
},
|
||||
]}
|
||||
value={segment}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default SegmentedHeader;
|
||||
|
|
@ -27,6 +27,8 @@ import { closeJSActionTab } from "actions/jsActionActions";
|
|||
import { closeQueryActionTab } from "actions/pluginActionActions";
|
||||
import { getCurrentBasePageId } from "selectors/editorSelectors";
|
||||
import { getCurrentEntityInfo } from "../utils";
|
||||
import { useEditorType } from "ee/hooks";
|
||||
import { useParentEntityInfo } from "ee/hooks/datasourceEditorHooks";
|
||||
|
||||
export const useCurrentEditorState = () => {
|
||||
const [selectedSegment, setSelectedSegment] = useState<EditorEntityTab>(
|
||||
|
|
@ -58,7 +60,9 @@ export const useCurrentEditorState = () => {
|
|||
export const useSegmentNavigation = (): {
|
||||
onSegmentChange: (value: string) => void;
|
||||
} => {
|
||||
const basePageId = useSelector(getCurrentBasePageId);
|
||||
const editorType = useEditorType(location.pathname);
|
||||
const { parentEntityId: baseParentEntityId } =
|
||||
useParentEntityInfo(editorType);
|
||||
|
||||
/**
|
||||
* Callback to handle the segment change
|
||||
|
|
@ -70,17 +74,17 @@ export const useSegmentNavigation = (): {
|
|||
const onSegmentChange = (value: string) => {
|
||||
switch (value) {
|
||||
case EditorEntityTab.QUERIES:
|
||||
history.push(queryListURL({ basePageId }), {
|
||||
history.push(queryListURL({ baseParentEntityId }), {
|
||||
invokedBy: NavigationMethod.SegmentControl,
|
||||
});
|
||||
break;
|
||||
case EditorEntityTab.JS:
|
||||
history.push(jsCollectionListURL({ basePageId }), {
|
||||
history.push(jsCollectionListURL({ baseParentEntityId }), {
|
||||
invokedBy: NavigationMethod.SegmentControl,
|
||||
});
|
||||
break;
|
||||
case EditorEntityTab.UI:
|
||||
history.push(widgetListURL({ basePageId }), {
|
||||
history.push(widgetListURL({ baseParentEntityId }), {
|
||||
invokedBy: NavigationMethod.SegmentControl,
|
||||
});
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user