chore: Side by Side exits beta phase (#38347)

This commit is contained in:
Hetu Nandu 2024-12-30 10:41:52 +05:30 committed by GitHub
parent cf534946b6
commit aaabb154f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 228 additions and 12 deletions

View File

@ -35,6 +35,7 @@ import { AnvilSnapshot } from "../Pages/Anvil/AnvilSnapshot";
export class ObjectsRegistry {
private static aggregateHelper__: AggregateHelper;
static get AggregateHelper(): AggregateHelper {
if (ObjectsRegistry.aggregateHelper__ === undefined) {
ObjectsRegistry.aggregateHelper__ = new AggregateHelper();
@ -43,6 +44,7 @@ export class ObjectsRegistry {
}
private static assertHelper__: AssertHelper;
static get AssertHelper(): AssertHelper {
if (ObjectsRegistry.assertHelper__ === undefined) {
ObjectsRegistry.assertHelper__ = new AssertHelper();
@ -51,6 +53,7 @@ export class ObjectsRegistry {
}
private static jsEditor__: JSEditor;
static get JSEditor(): JSEditor {
if (ObjectsRegistry.jsEditor__ === undefined) {
ObjectsRegistry.jsEditor__ = new JSEditor();
@ -59,6 +62,7 @@ export class ObjectsRegistry {
}
private static commonLocators__: CommonLocators;
static get CommonLocators(): CommonLocators {
if (ObjectsRegistry.commonLocators__ === undefined) {
ObjectsRegistry.commonLocators__ = new CommonLocators();
@ -67,6 +71,7 @@ export class ObjectsRegistry {
}
private static entityExplorer__: EntityExplorer;
static get EntityExplorer(): EntityExplorer {
if (ObjectsRegistry.entityExplorer__ === undefined) {
ObjectsRegistry.entityExplorer__ = new EntityExplorer();
@ -75,6 +80,7 @@ export class ObjectsRegistry {
}
private static apiPage__: ApiPage;
static get ApiPage(): ApiPage {
if (ObjectsRegistry.apiPage__ === undefined) {
ObjectsRegistry.apiPage__ = new ApiPage();
@ -83,6 +89,7 @@ export class ObjectsRegistry {
}
private static adminSettings__: AdminSettings;
static get AdminSettings(): AdminSettings {
if (ObjectsRegistry.adminSettings__ === undefined) {
ObjectsRegistry.adminSettings__ = new AdminSettings();
@ -91,6 +98,7 @@ export class ObjectsRegistry {
}
private static homePage__: HomePage;
static get HomePage(): HomePage {
if (ObjectsRegistry.homePage__ === undefined) {
ObjectsRegistry.homePage__ = new HomePage();
@ -99,6 +107,7 @@ export class ObjectsRegistry {
}
private static dataSources__: DataSources;
static get DataSources(): DataSources {
if (ObjectsRegistry.dataSources__ === undefined) {
ObjectsRegistry.dataSources__ = new DataSources();
@ -107,6 +116,7 @@ export class ObjectsRegistry {
}
private static table__: Table;
static get Table(): Table {
if (ObjectsRegistry.table__ === undefined) {
ObjectsRegistry.table__ = new Table();
@ -115,6 +125,7 @@ export class ObjectsRegistry {
}
private static tabs__: Tabs;
static get Tabs(): Tabs {
if (ObjectsRegistry.tabs__ === undefined) {
ObjectsRegistry.tabs__ = new Tabs();
@ -123,6 +134,7 @@ export class ObjectsRegistry {
}
private static propertyPane__: PropertyPane;
static get PropertyPane(): PropertyPane {
if (ObjectsRegistry.propertyPane__ === undefined) {
ObjectsRegistry.propertyPane__ = new PropertyPane();
@ -131,6 +143,7 @@ export class ObjectsRegistry {
}
private static deployMode__: DeployMode;
static get DeployMode(): DeployMode {
if (ObjectsRegistry.deployMode__ === undefined) {
ObjectsRegistry.deployMode__ = new DeployMode();
@ -139,6 +152,7 @@ export class ObjectsRegistry {
}
private static gitSync__: GitSync;
static get GitSync(): GitSync {
if (ObjectsRegistry.gitSync__ === undefined) {
ObjectsRegistry.gitSync__ = new GitSync();
@ -147,6 +161,7 @@ export class ObjectsRegistry {
}
private static fakerHelper__: FakerHelper;
static get FakerHelper(): FakerHelper {
if (ObjectsRegistry.fakerHelper__ === undefined) {
ObjectsRegistry.fakerHelper__ = new FakerHelper();
@ -155,6 +170,7 @@ export class ObjectsRegistry {
}
private static debuggerHelper__: DebuggerHelper;
static get DebuggerHelper(): DebuggerHelper {
if (ObjectsRegistry.debuggerHelper__ === undefined) {
ObjectsRegistry.debuggerHelper__ = new DebuggerHelper();
@ -163,6 +179,7 @@ export class ObjectsRegistry {
}
private static appSettings__: AppSettings;
static get AppSettings(): AppSettings {
if (ObjectsRegistry.appSettings__ === undefined) {
ObjectsRegistry.appSettings__ = new AppSettings();
@ -171,6 +188,7 @@ export class ObjectsRegistry {
}
private static generalSettings__: GeneralSettings;
static get GeneralSettings(): GeneralSettings {
if (ObjectsRegistry.generalSettings__ === undefined) {
ObjectsRegistry.generalSettings__ = new GeneralSettings();
@ -179,6 +197,7 @@ export class ObjectsRegistry {
}
private static pageSettings__: PageSettings;
static get PageSettings(): PageSettings {
if (ObjectsRegistry.pageSettings__ === undefined) {
ObjectsRegistry.pageSettings__ = new PageSettings();
@ -187,6 +206,7 @@ export class ObjectsRegistry {
}
private static themeSettings__: ThemeSettings;
static get ThemeSettings(): ThemeSettings {
if (ObjectsRegistry.themeSettings__ === undefined) {
ObjectsRegistry.themeSettings__ = new ThemeSettings();
@ -195,6 +215,7 @@ export class ObjectsRegistry {
}
private static embedSettings__: EmbedSettings;
static get EmbedSettings(): EmbedSettings {
if (ObjectsRegistry.embedSettings__ === undefined) {
ObjectsRegistry.embedSettings__ = new EmbedSettings();
@ -203,6 +224,7 @@ export class ObjectsRegistry {
}
private static LibraryInstaller__: LibraryInstaller;
static get LibraryInstaller(): LibraryInstaller {
if (ObjectsRegistry.LibraryInstaller__ === undefined) {
ObjectsRegistry.LibraryInstaller__ = new LibraryInstaller();
@ -211,6 +233,7 @@ export class ObjectsRegistry {
}
private static peekOverlay__: PeekOverlay;
static get PeekOverlay(): PeekOverlay {
if (ObjectsRegistry.peekOverlay__ === undefined) {
ObjectsRegistry.peekOverlay__ = new PeekOverlay();
@ -219,6 +242,7 @@ export class ObjectsRegistry {
}
private static inviteModal__: InviteModal;
static get InviteModal(): InviteModal {
if (ObjectsRegistry.inviteModal__ === undefined) {
ObjectsRegistry.inviteModal__ = new InviteModal();
@ -227,6 +251,7 @@ export class ObjectsRegistry {
}
private static templates__: Templates;
static get Templates(): Templates {
if (ObjectsRegistry.templates__ === undefined) {
ObjectsRegistry.templates__ = new Templates();
@ -235,6 +260,7 @@ export class ObjectsRegistry {
}
private static onboarding__: Onboarding;
static get Onboarding(): Onboarding {
if (ObjectsRegistry.onboarding__ === undefined) {
ObjectsRegistry.onboarding__ = new Onboarding();
@ -243,6 +269,7 @@ export class ObjectsRegistry {
}
private static autoLayout__: AutoLayout;
static get AutoLayout(): AutoLayout {
if (ObjectsRegistry.autoLayout__ === undefined) {
ObjectsRegistry.autoLayout__ = new AutoLayout();
@ -251,6 +278,7 @@ export class ObjectsRegistry {
}
private static anvilLayout__: AnvilLayout;
static get AnvilLayout(): AnvilLayout {
if (ObjectsRegistry.anvilLayout__ === undefined) {
ObjectsRegistry.anvilLayout__ = new AnvilLayout();
@ -259,6 +287,7 @@ export class ObjectsRegistry {
}
private static wdsWidgets__: WDSWidgets;
static get WDSWidgets(): WDSWidgets {
if (ObjectsRegistry.wdsWidgets__ === undefined) {
ObjectsRegistry.wdsWidgets__ = new WDSWidgets();
@ -267,6 +296,7 @@ export class ObjectsRegistry {
}
private static anvilSnapshot__: AnvilSnapshot;
static get AnvilSnapshot(): AnvilSnapshot {
if (ObjectsRegistry.anvilSnapshot__ === undefined) {
ObjectsRegistry.anvilSnapshot__ = new AnvilSnapshot();
@ -275,6 +305,7 @@ export class ObjectsRegistry {
}
private static dataManager__: DataManager;
static get DataManager(): DataManager {
if (ObjectsRegistry.dataManager__ === undefined) {
ObjectsRegistry.dataManager__ = new DataManager();
@ -283,6 +314,7 @@ export class ObjectsRegistry {
}
private static gsheetHelper__: GsheetHelper;
static get GSheetHelper(): GsheetHelper {
if (ObjectsRegistry.gsheetHelper__ === undefined) {
ObjectsRegistry.gsheetHelper__ = new GsheetHelper();
@ -291,6 +323,7 @@ export class ObjectsRegistry {
}
private static communityTemplates__: CommunityTemplates;
static get CommunityTemplates(): CommunityTemplates {
if (ObjectsRegistry.communityTemplates__ === undefined) {
ObjectsRegistry.communityTemplates__ = new CommunityTemplates();
@ -299,6 +332,7 @@ export class ObjectsRegistry {
}
private static partialImportExport__: PartialImportExport;
static get PartialImportExport(): PartialImportExport {
if (ObjectsRegistry.partialImportExport__ === undefined) {
ObjectsRegistry.partialImportExport__ = new PartialImportExport();
@ -311,6 +345,7 @@ export const initLocalstorageRegistry = () => {
cy.window().then((window) => {
window.localStorage.setItem("ShowCommentsButtonToolTip", "");
window.localStorage.setItem("updateDismissed", "true");
window.localStorage.setItem("NUDGE_SHOWN_SPLIT_PANE", "true");
});
localStorage.setItem("inDeployedMode", "false");
};

View File

@ -19,6 +19,7 @@ import { v4 as uuidv4 } from "uuid";
const dayjs = require("dayjs");
const loginPage = require("../locators/LoginPage.json");
import homePage from "../locators/HomePage";
dayjs.extend(advancedFormat);
const commonlocators = require("../locators/commonlocators.json");
@ -54,6 +55,7 @@ export const initLocalstorage = () => {
cy.window().then((window) => {
window.localStorage.setItem("ShowCommentsButtonToolTip", "");
window.localStorage.setItem("updateDismissed", "true");
window.localStorage.setItem("NUDGE_SHOWN_SPLIT_PANE", "true");
});
};

View File

@ -1,6 +1,6 @@
import React from "react";
import type { PopoverTriggerProps } from "@radix-ui/react-popover";
import { Close, Root, Trigger, Portal } from "@radix-ui/react-popover";
import { Close, Root, Trigger, Portal, Arrow } from "@radix-ui/react-popover";
import type { PopoverHeaderProps, PopoverContentProps } from "./Popover.types";
import {
@ -60,6 +60,7 @@ function PopoverContent({ size = "sm", ...props }: PopoverContentProps) {
sideOffset={4}
{...props}
>
{props.showArrow ? <Arrow /> : null}
{props.children}
</StyledContent>
</Portal>

View File

@ -20,4 +20,6 @@ export type PopoverContentProps = {
className?: string;
/** the minimum size of the popover */
size?: PopoverSize;
/** render an arrow pointing to the trigger */
showArrow?: boolean;
} & RadixPopoverContentProps;

View File

@ -0,0 +1,57 @@
import React, { useEffect, useState } from "react";
import { Flex, Popover, Text } from "@appsmith/ads";
import * as Styled from "./styles";
import type { Align, Side } from "@radix-ui/react-popper";
interface Props {
trigger: React.ReactNode;
onDismissClick: () => void;
message: string;
align?: Align;
side?: Side;
delayOpen?: number;
}
export const Nudge = (props: Props) => {
const [open, setOpen] = useState(false);
useEffect(
function handleDelayOpenOnMount() {
const timer = setTimeout(() => {
setOpen(true);
}, props.delayOpen || 0);
return () => clearTimeout(timer);
},
[props.delayOpen],
);
return (
<Popover open={open}>
<Styled.PopoverTrigger data-active={open}>
{props.trigger}
</Styled.PopoverTrigger>
<Styled.PopoverContent
align={props.align}
showArrow
side={props.side}
size="sm"
>
<Flex
alignItems="flex-start"
backgroundColor="var(--ads-v2-color-bg-emphasis-max)"
gap="spaces-2"
>
<Text color="var(--ads-v2-color-bg)" kind="heading-xs">
{props.message}
</Text>
<Styled.CloseIcon
name="close-line"
onClick={props.onDismissClick}
size="md"
/>
</Flex>
</Styled.PopoverContent>
</Popover>
);
};

View File

@ -0,0 +1 @@
export { Nudge } from "./Nudge";

View File

@ -0,0 +1,49 @@
import styled from "styled-components";
import {
Icon,
PopoverContent as ADSPopoverContent,
PopoverTrigger as ADSPopoverTrigger,
} from "@appsmith/ads";
export const PopoverContent = styled(ADSPopoverContent)`
background: var(--ads-v2-color-bg-emphasis-max);
box-shadow: 0 1px 20px 0 #4c56641c;
border: none;
transform-origin: var(--radix-popover-content-transform-origin);
animation: fadeIn 0.2s cubic-bezier(0, 0, 0.58, 1);
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
`;
export const PopoverTrigger = styled(ADSPopoverTrigger)`
border: 2px solid transparent !important;
&[data-active="true"] {
border: 2px solid var(--ads-v2-color-blue-300) !important;
}
transition: border 0.2s cubic-bezier(0, 0, 0.58, 1);
`;
export const CloseIcon = styled(Icon)`
svg {
path {
fill: var(--ads-v2-color-bg);
}
}
padding: var(--ads-v2-spaces-2);
cursor: pointer;
border-radius: var(--ads-v2-border-radius);
&:hover {
background-color: #ffffff33;
}
`;

View File

@ -4,6 +4,8 @@ import localStorage, { LOCAL_STORAGE_KEYS } from "utils/localStorage";
import { SPLITPANE_ANNOUNCEMENT, createMessage } from "ee/constants/messages";
import { getAssetUrl } from "ee/utils/airgapHelpers";
import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
const Announcement = () => {
const localStorageFlag =
@ -22,6 +24,10 @@ const Announcement = () => {
);
};
const featureIsOutOfBeta = useFeatureFlag(
FEATURE_FLAG.release_actions_redesign_enabled,
);
const modalFooter = () => (
<>
<Button
@ -38,6 +44,11 @@ const Announcement = () => {
</>
);
// If the feature is out of beta, don't show the announcement
if (featureIsOutOfBeta) {
return null;
}
return (
<AnnouncementModal
banner={getAssetUrl(`${ASSETS_CDN_URL}/splitpane-banner.svg`)}

View File

@ -1,4 +1,4 @@
import React, { useCallback } from "react";
import React, { useCallback, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Button, Tooltip } from "@appsmith/ads";
@ -14,6 +14,8 @@ import { setIdeEditorViewMode } from "actions/ideActions";
import type { AppState } from "ee/reducers";
import { selectFeatureFlagCheck } from "ee/selectors/featureFlagsSelectors";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import { Nudge } from "IDE/Components/Nudge";
import { useShowSideBySideNudge } from "../hooks";
export const ScreenModeToggle = () => {
const dispatch = useDispatch();
@ -41,11 +43,15 @@ export const ScreenModeToggle = () => {
}
}, [dispatch, isAnimatedIDEEnabled]);
const [showNudge, dismissNudge] = useShowSideBySideNudge();
const switchToSplitScreen = useCallback(() => {
AnalyticsUtil.logEvent("EDITOR_MODE_CHANGE", {
to: EditorViewMode.SplitScreen,
});
dismissNudge();
if ("startViewTransition" in document && isAnimatedIDEEnabled) {
document.startViewTransition(() => {
dispatch(setIdeEditorViewMode(EditorViewMode.SplitScreen));
@ -53,7 +59,22 @@ export const ScreenModeToggle = () => {
} else {
dispatch(setIdeEditorViewMode(EditorViewMode.SplitScreen));
}
}, [dispatch, isAnimatedIDEEnabled]);
}, [dispatch, dismissNudge, isAnimatedIDEEnabled]);
const minimiseButton = useMemo(
() => (
<Button
className="ml-auto !min-w-[24px]"
data-testid={"t--ide-minimize"}
id={"editor-mode-minimize"}
isIconButton
kind="tertiary"
onClick={switchToSplitScreen}
startIcon={"minimize-v3"}
/>
),
[switchToSplitScreen],
);
if (ideViewMode === EditorViewMode.SplitScreen) {
return (
@ -74,20 +95,26 @@ export const ScreenModeToggle = () => {
);
}
if (showNudge) {
return (
<Nudge
align="center"
delayOpen={500}
message="Write code and configure UI elements side by side"
onDismissClick={dismissNudge}
side="left"
trigger={minimiseButton}
/>
);
}
return (
<Tooltip
content={createMessage(MINIMIZE_BUTTON_TOOLTIP)}
key={createMessage(MINIMIZE_BUTTON_TOOLTIP)}
placement="left"
>
<Button
className="ml-auto !min-w-[24px]"
data-testid={"t--ide-minimize"}
id={"editor-mode-minimize"}
isIconButton
kind="tertiary"
onClick={switchToSplitScreen}
startIcon={"minimize-v3"}
/>
{minimiseButton}
</Tooltip>
);
};

View File

@ -29,6 +29,11 @@ import { getCurrentBasePageId } from "selectors/editorSelectors";
import { getCurrentEntityInfo } from "../utils";
import { useEditorType } from "ee/hooks";
import { useParentEntityInfo } from "ee/hooks/datasourceEditorHooks";
import { useBoolean } from "usehooks-ts";
import { isWidgetActionConnectionPresent } from "selectors/onboardingSelectors";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import localStorage, { LOCAL_STORAGE_KEYS } from "utils/localStorage";
export const useCurrentEditorState = () => {
const [selectedSegment, setSelectedSegment] = useState<EditorEntityTab>(
@ -198,3 +203,26 @@ export const useIDETabClickHandlers = () => {
return { addClickHandler, tabClickHandler, closeClickHandler };
};
export const useShowSideBySideNudge: () => [boolean, () => void] = () => {
const widgetBindingsExist = useSelector(isWidgetActionConnectionPresent);
const localStorageFlag = localStorage.getItem(
LOCAL_STORAGE_KEYS.NUDGE_SHOWN_SPLIT_PANE,
);
const isActionRedesignEnabled = useFeatureFlag(
FEATURE_FLAG.release_actions_redesign_enabled,
);
const { setFalse, value } = useBoolean(
widgetBindingsExist && isActionRedesignEnabled && !localStorageFlag,
);
const dismissNudge = useCallback(() => {
setFalse();
localStorage.setItem(LOCAL_STORAGE_KEYS.NUDGE_SHOWN_SPLIT_PANE, "true");
}, [setFalse]);
return [value, dismissNudge];
};

View File

@ -10,10 +10,12 @@ import { toast } from "@appsmith/ads";
export const LOCAL_STORAGE_KEYS = {
CANVAS_CARDS_STATE: "CANVAS_CARDS_STATE",
SPLITPANE_ANNOUNCEMENT: "SPLITPANE_ANNOUNCEMENT",
NUDGE_SHOWN_SPLIT_PANE: "NUDGE_SHOWN_SPLIT_PANE",
};
class LocalStorageNotSupportedError extends Error {
name: string;
constructor() {
super();
this.name = "LOCAL_STORAGE_NOT_SUPPORTED";
@ -29,6 +31,7 @@ class WebStorage {
this._isSupported = this.isSupported();
}
// ref: https://github.com/Modernizr/Modernizr/blob/94592f279a410436530c7c06acc42a6e90c20150/feature-detects/storage/localstorage.js
isSupported = () => {
try {