feat: Open doc links in a new tab (#22613)

## Description

When user clicks on a docs link, open the docs in a new tab instead of
the omnibar.

Fixes #22409


Media
> A video or a GIF is preferred. when using Loom, don’t embed because it
looks like it’s a GIF. instead, just link to the video


## Type of change

- New feature (non-breaking change which adds functionality)


## How Has This Been Tested?
> Please describe the tests that you ran to verify your changes. Provide
instructions, so we can reproduce.
> Please also list any relevant details for your test configuration.
> Delete anything that is not important

- Manual
- Jest
- Cypress

### Test Plan
> Add Testsmith test cases links that relate to this PR

### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)


## Checklist:
### Dev activity
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag


### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
This commit is contained in:
Hetu Nandu 2023-04-26 21:33:13 +05:30 committed by GitHub
parent b3d9e56664
commit 3408729df9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 88 additions and 88 deletions

View File

@ -13,12 +13,11 @@ describe("Omnibar functionality test cases", () => {
cy.addDsl(dsl);
});
it("1. Bug #15104 The Data is not displayed in Omnibar after clicking on learn more link from property pane", function () {
it("1. Docs tab opens after clicking on learn more link from property pane", function () {
cy.dragAndDropToCanvas("audiowidget", { x: 300, y: 500 });
cy.xpath('//span[text()="Learn more"]').click();
cy.get(locators._omnibarDescription).scrollTo("top");
cy.get(omnibar.openDocumentationLink);
cy.get("body").click(0, 0);
ObjectsRegistry.AggregateHelper.AssertNewTabOpened(() => {
cy.xpath('//span[text()="Learn more"]').click();
});
});
it("2.Verify omnibar is present across all pages and validate its fields", function () {

View File

@ -15,10 +15,10 @@ describe("API Panel Test Functionality ", function () {
cy.get("body").click(0, 0);
ee.ExpandCollapseEntity("Queries/JS");
ee.ActionContextMenuByEntityName("FirstAPI", "Copy to page", "SecondPage");
// click on learn how link
cy.get(".t--learn-how-apis-link").click();
// this should open in a global search modal
cy.get(commonlocators.globalSearchModal);
ObjectsRegistry.AggregateHelper.AssertNewTabOpened(() => {
// click on learn how link
cy.get(".t--learn-how-apis-link").click();
});
cy.get("body").click(0, 0);
ee.ActionContextMenuByEntityName("FirstAPICopy", "Move to page", "Page1");
cy.wait(2000);

View File

@ -8,11 +8,9 @@ describe("Check datasource doc links", function () {
cy.get("@dsName").then(($dsName) => {
dsName = $dsName;
_.dataSources.CreateQueryAfterDSSaved();
_.agHelper.GetNClick(_.dataSources._queryDoc);
_.agHelper.AssertElementVisible(_.dataSources._globalSearchModal);
_.agHelper.AssertElementVisible(
_.dataSources._globalSearchInput("PostgreSQL"),
);
_.agHelper.AssertNewTabOpened(() => {
_.agHelper.GetNClick(_.dataSources._queryDoc);
});
});
});
@ -21,11 +19,9 @@ describe("Check datasource doc links", function () {
cy.get("@dsName").then(($dsName) => {
dsName = $dsName;
_.dataSources.CreateQueryAfterDSSaved();
_.agHelper.GetNClick(_.dataSources._queryDoc);
_.agHelper.AssertElementVisible(_.dataSources._globalSearchModal);
_.agHelper.AssertElementVisible(
_.dataSources._globalSearchInput("MongoDB"),
);
_.agHelper.AssertNewTabOpened(() => {
_.agHelper.GetNClick(_.dataSources._queryDoc);
});
});
});
@ -34,11 +30,9 @@ describe("Check datasource doc links", function () {
cy.get("@dsName").then(($dsName) => {
dsName = $dsName;
_.dataSources.CreateQueryAfterDSSaved();
_.agHelper.GetNClick(_.dataSources._queryDoc);
_.agHelper.AssertElementVisible(_.dataSources._globalSearchModal);
_.agHelper.AssertElementVisible(
_.dataSources._globalSearchInput("MySQL"),
);
_.agHelper.AssertNewTabOpened(() => {
_.agHelper.GetNClick(_.dataSources._queryDoc);
});
});
});

View File

@ -1169,6 +1169,18 @@ export class AggregateHelper {
}
}
public AssertNewTabOpened(openTabFunc: () => void) {
cy.window().then((win) => {
cy.spy(win, "open").as("windowOpen");
openTabFunc();
cy.get("@windowOpen").should(
"be.calledWith",
Cypress.sinon.match.string,
"_blank",
);
});
}
//Not used:
// private xPathToCss(xpath: string) {
// return xpath

View File

@ -0,0 +1,28 @@
import AnalyticsUtil from "../utils/AnalyticsUtil";
export enum DocsLink {
CAPTURE_DATA = "CAPTURE_DATA",
WHITELIST_IP = "WHITELIST_IP",
CONNECT_DATA = "CONNECT_DATA",
QUERY = "QUERY",
}
const LinkData: Record<DocsLink, string> = {
CONNECT_DATA:
"https://docs.appsmith.com/core-concepts/connecting-to-data-sources",
QUERY:
"https://docs.appsmith.com/core-concepts/connecting-to-data-sources#docusaurus_skipToContent_fallback",
WHITELIST_IP:
"https://docs.appsmith.com/core-concepts/connecting-to-data-sources/connecting-to-databases",
CAPTURE_DATA:
"https://docs.appsmith.com/core-concepts/data-access-and-binding/capturing-data-write",
};
export const openDoc = (type: DocsLink, link?: string, subType?: string) => {
let linkToOpen = LinkData[type];
if (link && link.length) {
linkToOpen = link;
}
AnalyticsUtil.logEvent("OPEN_DOCS", { source: type, queryType: subType });
window.open(linkToOpen, "_blank");
};

View File

@ -1,8 +1,8 @@
import React, { useCallback, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
HTTP_METHOD_OPTIONS,
API_EDITOR_TABS,
HTTP_METHOD_OPTIONS,
} from "constants/ApiEditorConstants/CommonApiConstants";
import { GRAPHQL_HTTP_METHOD_OPTIONS } from "constants/ApiEditorConstants/GraphQLEditorConstants";
import styled from "styled-components";
@ -11,10 +11,6 @@ import FormRow from "components/editorComponents/FormRow";
import type { PaginationField, SuggestedWidget } from "api/ActionAPI";
import type { Action, PaginationType } from "entities/Action";
import { isGraphqlPlugin, SlashCommand } from "entities/Action";
import {
setGlobalSearchQuery,
toggleShowGlobalSearchModal,
} from "actions/globalSearchActions";
import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray";
import ApiResponseView from "components/editorComponents/ApiResponseView";
import EmbeddedDatasourcePathField from "components/editorComponents/form/fields/EmbeddedDatasourcePathField";
@ -48,7 +44,6 @@ import {
createMessage,
WIDGET_BIND_HELP,
} from "@appsmith/constants/messages";
import AnalyticsUtil from "utils/AnalyticsUtil";
import CloseEditor from "components/editorComponents/CloseEditor";
import { useParams } from "react-router";
import DataSourceList from "./ApiRightPane";
@ -67,11 +62,14 @@ import {
hasExecuteActionPermission,
hasManageActionPermission,
} from "@appsmith/utils/permissionHelpers";
import { executeCommandAction } from "actions/apiPaneActions";
import {
executeCommandAction,
setApiPaneConfigSelectedTabIndex,
} from "actions/apiPaneActions";
import { getApiPaneConfigSelectedTabIndex } from "selectors/apiPaneSelectors";
import { setApiPaneConfigSelectedTabIndex } from "actions/apiPaneActions";
import type { AutoGeneratedHeader } from "./helpers";
import { showDebuggerFlag } from "selectors/debuggerSelectors";
import { DocsLink, openDoc } from "../../../constants/DocumentationLinks";
const Form = styled.form`
position: relative;
@ -706,9 +704,7 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) {
const theme = EditorTheme.LIGHT;
const handleClickLearnHow = (e: React.MouseEvent) => {
e.stopPropagation();
dispatch(setGlobalSearchQuery("capturing data"));
dispatch(toggleShowGlobalSearchModal());
AnalyticsUtil.logEvent("OPEN_OMNIBAR", { source: "LEARN_HOW_DATASOURCE" });
openDoc(DocsLink.CAPTURE_DATA);
};
function handleSearchSnippetClick() {

View File

@ -13,7 +13,6 @@ import type { InjectedFormProps } from "redux-form";
import { reduxForm } from "redux-form";
import { APPSMITH_IP_ADDRESSES } from "constants/DatasourceEditorConstants";
import { getAppsmithConfigs } from "@appsmith/configs";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { convertArrayToSentence } from "utils/helpers";
import { PluginType } from "entities/Action";
import type { AppState } from "@appsmith/reducers";
@ -35,12 +34,12 @@ import Debugger, {
} from "./Debugger";
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
import { showDebuggerFlag } from "selectors/debuggerSelectors";
import { DocsLink, openDoc } from "../../../constants/DocumentationLinks";
const { cloudHosting } = getAppsmithConfigs();
interface DatasourceDBEditorProps extends JSONtoFormProps {
setDatasourceViewMode: (viewMode: boolean) => void;
openOmnibarReadMore: (text: string) => void;
datasourceId: string;
applicationId: string;
pageId: string;
@ -101,10 +100,8 @@ class DatasourceDBEditor extends JSONtoForm<Props> {
});
};
openOmnibarReadMore = () => {
const { openOmnibarReadMore } = this.props;
openOmnibarReadMore("connect to databases");
AnalyticsUtil.logEvent("OPEN_OMNIBAR", { source: "READ_MORE_DATASOURCE" });
openDocumentation = () => {
openDoc(DocsLink.WHITELIST_IP);
};
render() {
@ -188,7 +185,7 @@ class DatasourceDBEditor extends JSONtoForm<Props> {
<span>{`Whitelist the IP ${convertArrayToSentence(
APPSMITH_IP_ADDRESSES,
)} on your database instance to connect to it. `}</span>
<a onClick={this.openOmnibarReadMore}>
<a onClick={this.openDocumentation}>
{"Learn more "}
<StyledOpenDocsIcon icon="document-open" />
</a>

View File

@ -28,8 +28,6 @@ import RestAPIDatasourceForm from "./RestAPIDatasourceForm";
import type { Datasource } from "entities/Datasource";
import type { RouteComponentProps } from "react-router";
import EntityNotFoundPane from "pages/Editor/EntityNotFoundPane";
import { setGlobalSearchQuery } from "actions/globalSearchActions";
import { toggleShowGlobalSearchModal } from "actions/globalSearchActions";
import { DatasourceComponentTypes } from "api/PluginApi";
import DatasourceSaasForm from "../SaaSEditor/DatasourceForm";
@ -166,7 +164,6 @@ class DataSourceEditor extends React.Component<Props> {
isNewDatasource,
isSaving,
isTesting,
openOmnibarReadMore,
pageId,
pluginId,
pluginImages,
@ -189,7 +186,6 @@ class DataSourceEditor extends React.Component<Props> {
isNewDatasource={isNewDatasource}
isSaving={isSaving}
isTesting={isTesting}
openOmnibarReadMore={openOmnibarReadMore}
pageId={pageId}
pluginImage={pluginImages[pluginId]}
pluginType={pluginType}
@ -203,7 +199,6 @@ class DataSourceEditor extends React.Component<Props> {
export interface DatasourcePaneFunctions {
switchDatasource: (id: string) => void;
setDatasourceViewMode: (viewMode: boolean) => void;
openOmnibarReadMore: (text: string) => void;
discardTempDatasource: () => void;
deleteTempDSFromDraft: () => void;
toggleSaveActionFlag: (flag: boolean) => void;
@ -542,10 +537,6 @@ const mapDispatchToProps = (
},
setDatasourceViewMode: (viewMode: boolean) =>
dispatch(setDatasourceViewMode(viewMode)),
openOmnibarReadMore: (text: string) => {
dispatch(setGlobalSearchQuery(text));
dispatch(toggleShowGlobalSearchModal());
},
discardTempDatasource: () => dispatch(removeTempDatasource()),
deleteTempDSFromDraft: () => dispatch(deleteTempDSFromDraft()),
toggleSaveActionFlag: (flag) => dispatch(toggleSaveActionFlag(flag)),

View File

@ -1,18 +1,15 @@
import React, { useCallback } from "react";
import React from "react";
import { Button, Category, getTypographyByKey, Size } from "design-system-old";
import type { AppState } from "@appsmith/reducers";
import styled from "styled-components";
import { useDispatch, useSelector } from "react-redux";
import { useSelector } from "react-redux";
import { INTEGRATION_EDITOR_MODES, INTEGRATION_TABS } from "constants/routes";
import history from "utils/history";
import {
setGlobalSearchQuery,
toggleShowGlobalSearchModal,
} from "actions/globalSearchActions";
import AnalyticsUtil from "utils/AnalyticsUtil";
import type { WidgetType } from "constants/WidgetConstants";
import { integrationEditorURL } from "RouteBuilder";
import { getCurrentPageId } from "selectors/editorSelectors";
import { DocsLink, openDoc } from "../../../constants/DocumentationLinks";
const StyledDiv = styled.div`
color: ${(props) => props.theme.colors.propertyPane.ctaTextColor};
@ -54,15 +51,7 @@ type ConnectDataCTAProps = {
};
function ConnectDataCTA(props: ConnectDataCTAProps) {
const dispatch = useDispatch();
const pageId: string = useSelector(getCurrentPageId);
const openHelpModal = useCallback(() => {
dispatch(setGlobalSearchQuery("Connecting to Data Sources"));
dispatch(toggleShowGlobalSearchModal());
AnalyticsUtil.logEvent("OPEN_OMNIBAR", {
source: "PROPERTY_PANE_CONNECT_DATA",
});
}, []);
const onClick = () => {
const { widgetId, widgetTitle, widgetType } = props;
@ -93,7 +82,7 @@ function ConnectDataCTA(props: ConnectDataCTAProps) {
/>
<Button
category={Category.secondary}
onClick={openHelpModal}
onClick={() => openDoc(DocsLink.CONNECT_DATA)}
tabIndex={0}
tag="button"
text="Learn more"

View File

@ -48,8 +48,6 @@ import Resizable, {
} from "components/editorComponents/Debugger/Resizer";
import AnalyticsUtil from "utils/AnalyticsUtil";
import CloseEditor from "components/editorComponents/CloseEditor";
import { setGlobalSearchQuery } from "actions/globalSearchActions";
import { toggleShowGlobalSearchModal } from "actions/globalSearchActions";
import EntityDeps from "components/editorComponents/Debugger/EntityDependecies";
import {
checkIfSectionCanRender,
@ -60,20 +58,20 @@ import {
updateEvaluatedSectionConfig,
} from "components/formControls/utils";
import {
ACTION_EDITOR_REFRESH,
ACTION_EXECUTION_MESSAGE,
ACTION_RUN_BUTTON_MESSAGE_FIRST_HALF,
ACTION_RUN_BUTTON_MESSAGE_SECOND_HALF,
CREATE_NEW_DATASOURCE,
createMessage,
DEBUGGER_ERRORS,
DEBUGGER_LOGS,
DOCUMENTATION,
DOCUMENTATION_TOOLTIP,
INSPECT_ENTITY,
ACTION_EXECUTION_MESSAGE,
UNEXPECTED_ERROR,
NO_DATASOURCE_FOR_QUERY,
ACTION_EDITOR_REFRESH,
INVALID_FORM_CONFIGURATION,
ACTION_RUN_BUTTON_MESSAGE_FIRST_HALF,
ACTION_RUN_BUTTON_MESSAGE_SECOND_HALF,
CREATE_NEW_DATASOURCE,
DEBUGGER_ERRORS,
NO_DATASOURCE_FOR_QUERY,
UNEXPECTED_ERROR,
} from "@appsmith/constants/messages";
import { useParams } from "react-router";
import type { AppState } from "@appsmith/reducers";
@ -102,15 +100,15 @@ import { EDITOR_TABS } from "constants/QueryEditorConstants";
import type { FormEvalOutput } from "reducers/evaluationReducers/formEvaluationReducer";
import { isValidFormConfig } from "reducers/evaluationReducers/formEvaluationReducer";
import {
responseTabComponent,
InlineButton,
apiReactJsonProps,
CancelRequestButton,
LoadingOverlayContainer,
handleCancelActionExecution,
InlineButton,
LoadingOverlayContainer,
responseTabComponent,
ResponseTabErrorContainer,
ResponseTabErrorContent,
ResponseTabErrorDefaultMessage,
apiReactJsonProps,
} from "components/editorComponents/ApiResponseView";
import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScreen";
import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
@ -145,6 +143,7 @@ import LOG_TYPE from "entities/AppsmithConsole/logtype";
import type { SourceEntity } from "entities/AppsmithConsole";
import { ENTITY_TYPE as SOURCE_ENTITY_TYPE } from "entities/AppsmithConsole";
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
import { DocsLink, openDoc } from "../../../constants/DocumentationLinks";
import { AIWindow } from "@appsmith/components/editorComponents/GPT";
const QueryFormContainer = styled.form`
@ -613,13 +612,7 @@ export function EditorJSONtoForm(props: Props) {
const handleDocumentationClick = (e: React.MouseEvent) => {
e.stopPropagation();
const query = plugin?.name || "Connecting to datasources";
dispatch(setGlobalSearchQuery(query));
dispatch(toggleShowGlobalSearchModal());
AnalyticsUtil.logEvent("OPEN_OMNIBAR", {
source: "DATASOURCE_DOCUMENTATION_CLICK",
query,
});
openDoc(DocsLink.QUERY, plugin?.documentationLink, plugin?.name);
};
// Added function to handle the render of the configs

View File

@ -293,7 +293,8 @@ export type EventName =
| "APP_SETTINGS_SECTION_CLICK"
| APP_NAVIGATION_EVENT_NAMES
| ACTION_SELECTOR_EVENT_NAMES
| "PRETTIFY_AND_SAVE_KEYBOARD_SHORTCUT";
| "PRETTIFY_AND_SAVE_KEYBOARD_SHORTCUT"
| "OPEN_DOCS";
export type LIBRARY_EVENTS =
| "INSTALL_LIBRARY"