diff --git a/app/client/cypress/locators/HelpLocators.json b/app/client/cypress/locators/HelpLocators.json new file mode 100644 index 0000000000..0dfb70b900 --- /dev/null +++ b/app/client/cypress/locators/HelpLocators.json @@ -0,0 +1,10 @@ +{ + "HelpButton":"t--helpGlobalButton", + "DocSearchResult": "t--docHit", + "DocSearchResultTitle": "t--docHitTitle", + "DocSearchResultDesc": "t--docHitDesc", + "DocSearchResultOpenLink": "t--docOpenLink", + "DocSearchModal": "t--docSearchModal", + "DocMinimise": "t--docsMinimize", + "DocSearch": "ais-SearchBox-input" +} \ No newline at end of file diff --git a/app/client/cypress/locators/commonlocators.json b/app/client/cypress/locators/commonlocators.json index 5fe3ab7010..ec60a24566 100644 --- a/app/client/cypress/locators/commonlocators.json +++ b/app/client/cypress/locators/commonlocators.json @@ -1,5 +1,6 @@ { "editIcon": ".t--widget-propertypane-toggle", + "helpIcon": ".t--widget-help-control", "editPropCrossButton": ".t--property-pane-close-btn", "deleteWidgetIcon": ".t--widget-delete-control", "dropdownSelectButton": ".t--open-dropdown-Select-Action", diff --git a/app/client/gitbook-algolia-lambda.js b/app/client/gitbook-algolia-lambda.js new file mode 100644 index 0000000000..5cd91a6189 --- /dev/null +++ b/app/client/gitbook-algolia-lambda.js @@ -0,0 +1,117 @@ +var https = require("https"); +const algoliasearch = require("algoliasearch"); + +const client = algoliasearch("AZ2Z9CJSJ0", "e92300d4e8dbaf2cbaa9ebbbeb4e06e6"); +const index = client.initIndex("test_appsmith"); + +var options = { + headers: { + Authorization: + "Bearer aEhCb3hxVzVYWFJCY0g4b1owZmdKcTdJUmc0MjotTTY5cHFwVDlxTWx0cFU2akh4MC0tTTY5cHFwVWFobjBaRWNzTjh0WA==", + Cookie: "__cfduid=d8bac210f6e11cb26a8cd921f77727e3f1588323072", + }, +}; + +console.log("Loading function"); + +function getPage(pageId) { + return new Promise((resolve, reject) => { + https.get( + `https://api-beta.gitbook.com/v1/spaces/-Lzuzdhj8LjrQPaeyCxr/content/v/master/id/${pageId}?format=markdown`, + options, + res => { + res.setEncoding("utf8"); + let rawData = ""; + res.on("data", chunk => { + rawData += chunk; + }); + res.on("end", () => { + try { + const parsedData = JSON.parse(rawData); + resolve(parsedData); + } catch (e) { + reject(e); + console.error(e.message); + } + }); + }, + ); + }); +} + +const pages = []; + +function pushChildPages(masterPage) { + if (masterPage.pages) { + masterPage.pages.forEach(page => { + page.path = masterPage.path + "/" + page.path; + pushChildPages(page); + page.pages = undefined; + pages.push(page); + }); + } +} + +exports.handler = async event => { + const response = await new Promise((resolve, reject) => { + const req = https.get( + "https://api-beta.gitbook.com/v1/spaces/-Lzuzdhj8LjrQPaeyCxr/content", + options, + res => { + res.setEncoding("utf8"); + let rawData = ""; + res.on("data", chunk => { + rawData += chunk; + }); + res.on("end", () => { + try { + const parsedData = JSON.parse(rawData); + + let masterPage = parsedData.variants[0].page; + + pushChildPages(masterPage); + masterPage.pages = undefined; + + pages.push(masterPage); + + let promises = pages.map(page => page.uid).map(getPage); + Promise.all(promises).then(updatedPages => { + updatedPages.forEach((page, index) => { + page.path = pages[index].path; + page.pages = undefined; + page.objectID = page.uid; + }); + + resolve({ + statusCode: 200, + body: JSON.stringify(updatedPages), + }); + + // index.replaceAllObjects(updatedPages, { + // autoGenerateObjectIDIfNotExist: true + // }).then(({ objectIDs }) => { + // console.log(objectIDs); + // resolve({ + // statusCode: 200, + // body: JSON.stringify(updatedPages) + // }) + // }).catch(e => { + // reject({ + // statusCode: 500, + // body: 'Algolia upload failed.' + // }) + // }) + }); + } catch (e) { + reject({ + statusCode: 500, + body: "Most probably gitbook getPage apis failed", + }); + } + }); + }, + ); + }); + + return response; +}; diff --git a/app/client/package.json b/app/client/package.json index 4bec0583f3..d0b2e9ab4f 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -15,6 +15,7 @@ "@blueprintjs/timezone": "^3.6.0", "@craco/craco": "^5.6.1", "@manaflair/redux-batch": "^1.0.0", + "@optimizely/optimizely-sdk": "^4.0.0", "@sentry/browser": "^5.6.3", "@sentry/webpack-plugin": "^1.10.0", "@syncfusion/ej2-react-grids": "^17.4.40", @@ -28,6 +29,7 @@ "@types/react": "^16.8.2", "@types/react-dom": "^16.8.0", "@types/react-helmet": "^5.0.14", + "@types/react-instantsearch-dom": "^6.3.0", "@types/react-redux": "^7.0.1", "@types/react-router-dom": "^5.1.2", "@types/styled-components": "^4.1.8", @@ -40,6 +42,7 @@ "@uppy/react": "^1.4.5", "@uppy/url": "^1.3.2", "@uppy/webcam": "^1.3.1", + "algoliasearch": "^4.2.0", "axios": "^0.18.0", "chance": "^1.1.3", "codemirror": "^5.50.0", @@ -51,6 +54,8 @@ "fusioncharts": "^3.15.0-sr.1", "history": "^4.10.1", "husky": "^3.0.5", + "instantsearch.css": "^7.4.2", + "instantsearch.js": "^4.4.1", "interweave": "^12.1.1", "interweave-autolink": "^4.0.1", "json-fn": "^1.1.1", @@ -76,6 +81,7 @@ "react-google-maps": "^9.4.5", "react-helmet": "^5.2.1", "react-infinite-scroller": "^1.2.4", + "react-instantsearch-dom": "^6.4.0", "react-json-view": "^1.19.1", "react-paginating": "^1.4.0", "react-redux": "^7.1.3", diff --git a/app/client/src/actions/helpActions.ts b/app/client/src/actions/helpActions.ts new file mode 100644 index 0000000000..cb05fbeb68 --- /dev/null +++ b/app/client/src/actions/helpActions.ts @@ -0,0 +1,14 @@ +import { ReduxActionTypes } from "constants/ReduxActionConstants"; + +export const setHelpDefaultRefinement = (payload: string) => { + return { + type: ReduxActionTypes.SET_DEFAULT_REFINEMENT, + payload, + }; +}; +export const setHelpModalVisibility = (payload: boolean) => { + return { + type: ReduxActionTypes.SET_HELP_MODAL_OPEN, + payload, + }; +}; diff --git a/app/client/src/assets/icons/control/help.svg b/app/client/src/assets/icons/control/help.svg new file mode 100644 index 0000000000..b2986f1fb7 --- /dev/null +++ b/app/client/src/assets/icons/control/help.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/help/openlink.svg b/app/client/src/assets/icons/help/openlink.svg new file mode 100644 index 0000000000..3a699553b4 --- /dev/null +++ b/app/client/src/assets/icons/help/openlink.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/client/src/components/designSystems/appsmith/NotificationIcon.tsx b/app/client/src/components/designSystems/appsmith/NotificationIcon.tsx new file mode 100644 index 0000000000..921c1fe213 --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/NotificationIcon.tsx @@ -0,0 +1,70 @@ +import styled from "styled-components"; +import { hexToRgb } from "utils/AppsmithUtils"; + +export default styled.span<{ + animate?: boolean; + width?: number; + height?: number; + color?: string; +}>` + &&& { + display: block; + width: ${props => props.width || 8}px; + height: ${props => props.height || 8}px; + border-radius: 50%; + background: ${props => props.color || props.theme.colors.notification}; + cursor: pointer; + box-shadow: 0 0 0 + rgba( + ${props => { + const rgb = hexToRgb(props.color || props.theme.colors.notification); + return `${rgb.r}, ${rgb.g}, ${rgb.b}`; + }}, + 0.4 + ); + animation: ${props => (!!props.animate ? "pulse 2s infinite" : "")}; + + &:hover { + animation: none; + } + + @keyframes pulse { + 0% { + box-shadow: 0 0 0 0 + rgba( + ${props => { + const rgb = hexToRgb( + props.color || props.theme.colors.notification, + ); + return `${rgb.r}, ${rgb.g}, ${rgb.b}`; + }}, + 0.4 + ); + } + 70% { + box-shadow: 0 0 0 15px + rgba( + ${props => { + const rgb = hexToRgb( + props.color || props.theme.colors.notification, + ); + return `${rgb.r}, ${rgb.g}, ${rgb.b}`; + }}, + 0 + ); + } + 100% { + box-shadow: 0 0 0 0 + rgba( + ${props => { + const rgb = hexToRgb( + props.color || props.theme.colors.notification, + ); + return `${rgb.r}, ${rgb.g}, ${rgb.b}`; + }}, + 0 + ); + } + } + } +`; diff --git a/app/client/src/components/designSystems/appsmith/help/CollapsibleHelp.tsx b/app/client/src/components/designSystems/appsmith/help/CollapsibleHelp.tsx new file mode 100644 index 0000000000..7bdd33d935 --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/help/CollapsibleHelp.tsx @@ -0,0 +1,52 @@ +import React, { ReactNode } from "react"; +// import { Button, Collapse, Pre } from "@blueprintjs/core"; +import { Icon } from "@blueprintjs/core"; +import styled from "constants/DefaultTheme"; + +interface CollapsibleHelpProps { + children?: ReactNode; +} + +const HelpIcon = styled(Icon)` + margin-right: 14.5px; + margin-left: 4px; + height: 100%; + background-color: #ef7541; + border: 2px solid rgba(239, 123, 99); + border-radius: 50%; +`; + +const Container = styled.div` + background-color: rgba(239, 123, 99, 0.15); + border-radius: 4px; + padding: 10px 10px; + width: 100%; + position: relative; + display: flex; + align-items: center; + + margin-top: 18px; + margin-bottom: 19px; + margin-left: 6px; + width: 89%; +`; + +const LeftBar = styled.div` + border: 1px solid #ef7541; + border-radius: 4px; + position: absolute; + top: 1px; + left: 0; + height: 96%; +`; + +export default function CollapsibleHelp(props: CollapsibleHelpProps) { + // const [isOpen, setIsOpen] = useState(false); + return ( + + + +
{props.children}
+
+ ); +} diff --git a/app/client/src/components/designSystems/appsmith/help/DocumentationSearch.tsx b/app/client/src/components/designSystems/appsmith/help/DocumentationSearch.tsx new file mode 100644 index 0000000000..ef1cf27da0 --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/help/DocumentationSearch.tsx @@ -0,0 +1,325 @@ +import React from "react"; +import algoliasearch from "algoliasearch/lite"; +import { + InstantSearch, + Hits, + SearchBox, + // Pagination, + Highlight, + // ClearRefinements, + // RefinementList, + Configure, +} from "react-instantsearch-dom"; + +// import "instantsearch.css/themes/reset.css"; +import "instantsearch.css/themes/algolia.css"; +// import "./search.css"; + +import PropTypes from "prop-types"; +import { Icon } from "@blueprintjs/core"; +import { useDispatch, useSelector } from "react-redux"; +import { + setHelpModalVisibility, + setHelpDefaultRefinement, +} from "actions/helpActions"; +import styled from "styled-components"; +import { HelpIcons } from "icons/HelpIcons"; +import { HelpBaseURL } from "constants/HelpConstants"; +import { getDefaultRefinement } from "selectors/helpSelectors"; + +const searchClient = algoliasearch( + "AZ2Z9CJSJ0", + "d113611dccb80ac14aaa72a6e3ac6d10", +); + +// const StyledBack = styled(Button)` +// position: absolute; +// top: 36px; +// left: 5px; +// z-index: 26; +// `; + +// const StyledAnchor = styled.a` +// position: absolute; +// right: 24px; +// top: 40px; +// z-index: 26; +// `; + +// const headerHeight = 91; + +// const OpenIcon = styled(Icon)` +// position: absolute; +// right: 0; +// top: 3px; +// color: #888; +// `; + +const OenLinkIcon = HelpIcons.OPEN_LINK; + +const StyledOpenLinkIcon = styled(OenLinkIcon)` + position: absolute; + right: 0; + top: 0; + color: #888; + width: 12px; + height: 12px; + svg { + width: 12px; + height: 12px; + } +`; +function Hit(props: any) { + // const dispatch = useDispatch(); + + return ( +
{ + window.open( + (props.hit.path as string).replace("master", HelpBaseURL), + "_blank", + ); + // console.log(props); + // dispatch( + // setHelpUrl( + // (props.hit.path as string).replace( + // "master", + // HelpBaseURL, + // ), + // ), + // ); + }} + > +
+
+ +
+ +
+ +
+ + {/* */} +
+
+ ); +} + +Hit.propTypes = { + hit: PropTypes.object.isRequired, +}; + +const Header = styled.div` + position: absolute; + width: 100%; + background: #363e44; + padding-bottom: 20px; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +`; + +const SearchContainer = styled.div` + height: 100%; + + .ais-SearchBox { + position: relative; + width: 316px; + height: 50px; + margin: 17px; + } + + .ais-SearchBox-form { + height: 100%; + } + + [class^="ais-"] { + font-size: inherit; + } + + .ais-Hits { + margin-top: 142px; + height: calc(100% - 142px); + overflow: auto; + border: 1px solid #d0d7dd; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + } + .ais-SearchBox-input { + height: 100%; + padding: 4px 48px; + padding-right: 14px; + } + + .ais-SearchBox-submitIcon { + width: 18px; + height: 18px; + } + + .ais-Pagination { + margin-top: 1em; + } + + .ais-Hits-list { + margin: 0; + } + .ais-Hits-item { + margin-bottom: 1em; + width: 100%; + margin: 0; + padding: 20px; + border: 0; + border-bottom: 1px solid #d0d7dd; + box-sizing: border-box; + box-shadow: none; + } + + .ais-Hits-item:hover { + background-color: #f8f9fa; + } + + .hit-name { + margin-bottom: 0.5em; + font-weight: 500; + font-size: 16px; + line-height: 23px; + color: #000000; + position: relative; + } + + .hit-description { + color: #888; + font-size: 14px; + margin-bottom: 0.5em; + font-style: normal; + font-weight: normal; + font-size: 12px; + line-height: 20px; + /*Making description two lines*/ + height: 42px; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; /* number of lines to show */ + -webkit-box-orient: vertical; + /*Making description two lines*/ + /* or 167% */ + + letter-spacing: 0.2px; + } + + .ais-Highlight-highlighted { + background-color: #ffb100; + } + .ais-SearchBox-submit { + left: 16px; + } +`; + +export default function DocumentationSearch(props: { hitsPerPage: number }) { + const dispatch = useDispatch(); + const defaultRefinement = useSelector(getDefaultRefinement); + + return ( + + { + dispatch(setHelpModalVisibility(false)); + dispatch(setHelpDefaultRefinement("")); + }} + > +
+ + +
+

+ + Documentation + +

+ + +
+ + +
+
+ {/*
+
+ { + dispatch(setHelpUrl("")); + }} + /> + + Open in docs + + +
+ +
*/} +
+ ); +} diff --git a/app/client/src/components/designSystems/appsmith/help/HelpModal.tsx b/app/client/src/components/designSystems/appsmith/help/HelpModal.tsx new file mode 100644 index 0000000000..57e434f94c --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/help/HelpModal.tsx @@ -0,0 +1,84 @@ +import React, { useContext } from "react"; +import DocumentationSearch from "components/designSystems/appsmith/help/DocumentationSearch"; +import Button from "components/editorComponents/Button"; + +import { useSelector } from "store"; +import { useDispatch } from "react-redux"; +import { + getHelpModalOpen, + getHelpModalDimensions, +} from "selectors/helpSelectors"; +import { + setHelpDefaultRefinement, + setHelpModalVisibility, +} from "actions/helpActions"; +import styled from "styled-components"; +import { IntentColors, theme } from "constants/DefaultTheme"; +import { Position } from "@blueprintjs/core"; +import ModalComponent from "components/designSystems/blueprint/ModalComponent"; +import { LayersContext } from "constants/Layers"; + +const HelpButton = styled(Button)<{ + highlight: boolean; + layer: number; +}>` + &&&&& { + position: absolute; + bottom: 46px; + right: 27px; + z-index: ${props => props.layer}; + background: ${props => + props.highlight ? "#231f20" : theme.colors.primaryDarker}; + width: 105px; + height: 40px; + border-radius: 134px; + color: white; + border: 0; + box-shadow: 2px 4px 5px #888888; + } +`; + +export function HelpModal() { + const isHelpModalOpen = useSelector(getHelpModalOpen); + const helpDimensions = useSelector(getHelpModalDimensions); + const helpModalOpen = useSelector(getHelpModalOpen); + const dispatch = useDispatch(); + const layers = useContext(LayersContext); + + return ( + <> + { + dispatch(setHelpModalVisibility(false)); + dispatch(setHelpDefaultRefinement("")); + }} + isOpen={isHelpModalOpen} + zIndex={layers.help} + > + + + { + dispatch(setHelpModalVisibility(!helpModalOpen)); + }} + /> + + ); +} diff --git a/app/client/src/components/designSystems/blueprint/CalloutComponent.tsx b/app/client/src/components/designSystems/blueprint/CalloutComponent.tsx index 2b245b1f7e..32fc40eb21 100644 --- a/app/client/src/components/designSystems/blueprint/CalloutComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/CalloutComponent.tsx @@ -2,9 +2,9 @@ import styled from "styled-components"; import { Callout } from "@blueprintjs/core"; import { Colors } from "constants/Colors"; -const CalloutComponent = styled(Callout)` +const CalloutComponent = styled(Callout)<{ background?: string }>` && { - background-color: ${Colors.WHITE_SMOKE}; + background-color: ${props => props.background || Colors.WHITE_SMOKE}; } `; diff --git a/app/client/src/components/designSystems/blueprint/ModalComponent.tsx b/app/client/src/components/designSystems/blueprint/ModalComponent.tsx index 7c01d19147..16a849e25c 100644 --- a/app/client/src/components/designSystems/blueprint/ModalComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/ModalComponent.tsx @@ -5,18 +5,21 @@ import { getCanvasClassName } from "utils/generators"; const Container = styled.div<{ width: number; height: number; + top?: number; + left?: number; + zIndex?: number; }>` &&& { .${Classes.OVERLAY} { .${Classes.OVERLAY_BACKDROP} { - z-index: 1; + z-index: ${props => props.zIndex}; } position: fixed; top: ${props => props.theme.headerHeight}; right: 0; bottom: 0; height: 100vh; - z-index: 1; + z-index: ${props => props.zIndex}; width: 100%; display: flex; justify-content: center; @@ -27,6 +30,9 @@ const Container = styled.div<{ min-height: ${props => props.height}px; background: white; border-radius: ${props => props.theme.radii[1]}px; + top: ${props => props.top}px; + left: ${props => props.left}px; + // z-index: ${props => props.zIndex}; } } } @@ -52,6 +58,10 @@ export type ModalComponentProps = { canEscapeKeyClose: boolean; scrollContents: boolean; height: number; + top?: number; + left?: number; + hasBackDrop?: boolean; + zIndex?: number; }; /* eslint-disable react/display-name */ @@ -65,7 +75,13 @@ export const ModalComponent = (props: ModalComponentProps) => { } }, [props.scrollContents]); return ( - + { canEscapeKeyClose={props.canEscapeKeyClose} usePortal={false} enforceFocus={false} + hasBackdrop={ + props.hasBackDrop !== undefined ? !!props.hasBackDrop : true + } >
- props.editorTheme === THEMES.DARK ? "#f7c75b" : "#ffb100"}; + props.editorTheme === THEMES.DARK + ? props.theme.colors.bindingTextDark + : props.theme.colors.bindingText}; font-weight: 700; } .CodeMirror { diff --git a/app/client/src/components/editorComponents/NavBarItem.tsx b/app/client/src/components/editorComponents/NavBarItem.tsx index 67c2cc327b..cac5492b9f 100644 --- a/app/client/src/components/editorComponents/NavBarItem.tsx +++ b/app/client/src/components/editorComponents/NavBarItem.tsx @@ -2,26 +2,45 @@ import React from "react"; import styled from "styled-components"; import { NavLink } from "react-router-dom"; import AnalyticsUtil from "utils/AnalyticsUtil"; +import NotificationIcon from "components/designSystems/appsmith/NotificationIcon"; +import { theme } from "constants/DefaultTheme"; type MenuBarItemProps = { icon: Function; path: string; title: string; exact: boolean; + width: number; + height: number; + external?: boolean; className?: string; + highlight?: boolean; + onClick?: Function; }; +// const AnmiatedNotificationIcon = + +const StyledNotificationIcon = styled(NotificationIcon)` + position: absolute; + top: -4px; + right: -3px; +`; + type Props = MenuBarItemProps; -const IconContainer = styled.div` +const IconContainer = styled.div<{ + width: number; + height: number; +}>` display: flex; align-items: center; justify-content: center; + margin: 0 auto; margin-bottom: 5px; background-color: ${props => props.theme.colors.menuButtonBGInactive}; border-radius: ${props => props.theme.radii[1]}px; - height: 32px; - width: 32px; + height: ${props => props.height}px; + width: ${props => props.width}px; svg path { fill: ${props => props.theme.colors.menuIconColorInactive}; } @@ -55,30 +74,80 @@ const ItemContainer = styled.div` } } } + span { + width: 100%; + display: inline-block; + text-align: center; + } } `; +const Anchor = styled.a` + width: 64px; + display: inline-block; +`; + +const ExternalLink = function(props: any) { + return ( + { + props.onClick && props.onClick(); + }} + href={props.to} + className={props.className} + target="_blank" + > + {props.children} + + ); +}; + +const DetailsContainer = styled.div` + position: relative; +`; + class NavBarItem extends React.Component { render(): React.ReactNode { - const { title, icon, path, exact } = this.props; - + const { + title, + icon, + path, + exact, + width, + height, + external, + highlight, + onClick, + } = this.props; + const Link = external ? ExternalLink : NavLink; return ( - { + onClick && onClick(); AnalyticsUtil.logEvent("SIDEBAR_NAVIGATION", { navPage: this.props.title.toUpperCase(), }); }} > - - {icon({ width: 24, height: 24 })} - {title} - - + + + {icon({ width: width - 8, height: height - 8 })} + + {title} + {highlight && ( + + )} + + ); } diff --git a/app/client/src/components/editorComponents/WidgetNameComponent.tsx b/app/client/src/components/editorComponents/WidgetNameComponent.tsx index 2075c3fdf9..d0825af18f 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent.tsx @@ -1,13 +1,13 @@ import React, { useContext } from "react"; import styled from "styled-components"; -import { useSelector } from "react-redux"; +import { useSelector, useDispatch } from "react-redux"; import { AppState } from "reducers"; import { ControlIcons } from "icons/ControlIcons"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; import { theme } from "constants/DefaultTheme"; import { Colors } from "constants/Colors"; import { PropertyPaneReduxState } from "reducers/uiReducers/propertyPaneReducer"; -import { Tooltip } from "@blueprintjs/core"; +import { Tooltip, Icon } from "@blueprintjs/core"; import { useShowPropertyPane, useWidgetSelection, @@ -15,6 +15,13 @@ import { import AnalyticsUtil from "utils/AnalyticsUtil"; import { WidgetOperations } from "widgets/BaseWidget"; import { WidgetType } from "constants/WidgetConstants"; +import { HelpMap, HelpBaseURL } from "constants/HelpConstants"; +import { + setHelpDefaultRefinement, + setHelpModalVisibility, +} from "actions/helpActions"; +import FeatureFlag from "utils/featureFlags"; +import { FeatureFlagsEnum } from "configs/types"; const CONTROL_ICON_SIZE = 20; @@ -38,6 +45,13 @@ const PositionStyle = styled.div<{ selected?: boolean }>` } `; +const HelpControl = styled.div` + position: absolute; + right: ${props => props.theme.spaces[13] * 2 - 10}px; + top: -${props => props.theme.spaces[2]}px; + cursor: pointer; +`; + const DeleteControl = styled.div` position: absolute; right: ${props => props.theme.spaces[13]}px; @@ -58,6 +72,26 @@ const deleteControlIcon = ( ); const EditIcon = ControlIcons.EDIT_CONTROL; +const HelpIcon = styled(Icon)<{ width: number; height: number }>` + width: ${props => props.width}px; + height: ${props => props.width}px; + color: ${Colors.SHARK}; + svg { + width: ${props => props.width}px; + height: ${props => props.width}px; + } +`; + +// const HelpIcon = ControlIcons.HELP_CONTROL; + +const helpControlIcon = ( + +); + type WidgetNameComponentProps = { widgetName?: string; widgetId: string; @@ -68,6 +102,7 @@ type WidgetNameComponentProps = { export const WidgetNameComponent = (props: WidgetNameComponentProps) => { const { updateWidget } = useContext(EditorContext); + const dispatch = useDispatch(); const showPropertyPane = useShowPropertyPane(); // Dispatch hook handy to set a widget as focused/selected const { selectWidget } = useWidgetSelection(); @@ -148,6 +183,20 @@ export const WidgetNameComponent = (props: WidgetNameComponentProps) => { return showWidgetName ? (
{props.widgetName}
+ {FeatureFlag.check(FeatureFlagsEnum.documentationV2) && ( + { + dispatch(setHelpDefaultRefinement(HelpMap[props.type].searchKey)); + dispatch(setHelpModalVisibility(true)); + // window.open(`${HelpBaseURL}${HelpMap[props.type]}`, "_blank"); + }} + > + + {helpControlIcon} + + + )} ({ enabled: false, key: "NZALSCjsaxOIyprzITLz2yZwFzQynGt1", }, + featureFlag: { + // remoteConfig: { + // optimizely: "PVDSYRhBhvUVY3tN6mkV1s", + // }, + default: { + documentationv2: true, + apipanev2: true, + datasourcepane: true, + querypane: true, + }, + }, }); export default autoConfig; diff --git a/app/client/src/configs/dev.config.ts b/app/client/src/configs/dev.config.ts index 3b2848b70b..ef9775f5bb 100644 --- a/app/client/src/configs/dev.config.ts +++ b/app/client/src/configs/dev.config.ts @@ -1,6 +1,5 @@ import { SENTRY_STAGE_CONFIG } from "constants/ThirdPartyConstants"; import { AppsmithUIConfigs } from "./types"; -import { FeatureFlagEnum } from "utils/featureFlags"; const devConfig = (baseUrl: string): AppsmithUIConfigs => ({ sentry: { @@ -14,14 +13,20 @@ const devConfig = (baseUrl: string): AppsmithUIConfigs => ({ enabled: false, key: "NZALSCjsaxOIyprzITLz2yZwFzQynGt1", }, + featureFlag: { + remoteConfig: { + optimizely: "PVDSYRhBhvUVY3tN6mkV1s", + }, + default: { + documentationv2: true, + apipanev2: true, + datasourcepane: true, + querypane: true, + }, + }, apiUrl: "/api/", baseUrl, logLevel: "debug", - featureFlags: [ - FeatureFlagEnum.ApiPaneV2, - FeatureFlagEnum.DatasourcePane, - FeatureFlagEnum.QueryPane, - ], }); export default devConfig; diff --git a/app/client/src/configs/prod.config.ts b/app/client/src/configs/prod.config.ts index 82f2e039c9..b5c8660f34 100644 --- a/app/client/src/configs/prod.config.ts +++ b/app/client/src/configs/prod.config.ts @@ -4,7 +4,6 @@ import { HOTJAR_PROD_HJSV, } from "constants/ThirdPartyConstants"; import { AppsmithUIConfigs } from "./types"; -import { FeatureFlagEnum } from "utils/featureFlags"; export const prodConfig = (baseUrl: string): AppsmithUIConfigs => ({ sentry: { @@ -25,11 +24,17 @@ export const prodConfig = (baseUrl: string): AppsmithUIConfigs => ({ apiUrl: "/api/", baseUrl, logLevel: "error", - featureFlags: [ - FeatureFlagEnum.ApiPaneV2, - FeatureFlagEnum.DatasourcePane, - FeatureFlagEnum.QueryPane, - ], + featureFlag: { + remoteConfig: { + optimizely: "Jq3K2kVdvuvxecHyHbVYcj", + }, + default: { + documentationv2: true, + apipanev2: true, + datasourcepane: true, + querypane: true, + }, + }, }); export default prodConfig; diff --git a/app/client/src/configs/stage.config.ts b/app/client/src/configs/stage.config.ts index 2dd580a1b2..572655a732 100644 --- a/app/client/src/configs/stage.config.ts +++ b/app/client/src/configs/stage.config.ts @@ -1,6 +1,5 @@ import { SENTRY_STAGE_CONFIG } from "constants/ThirdPartyConstants"; import { AppsmithUIConfigs } from "./types"; -import { FeatureFlagEnum } from "utils/featureFlags"; const stageConfig = (baseUrl: string): AppsmithUIConfigs => ({ sentry: { @@ -14,14 +13,20 @@ const stageConfig = (baseUrl: string): AppsmithUIConfigs => ({ enabled: true, key: "NZALSCjsaxOIyprzITLz2yZwFzQynGt1", }, + featureFlag: { + remoteConfig: { + optimizely: "2qP3XSwgM9pHYTEYbtbAQx", + }, + default: { + documentationv2: true, + apipanev2: true, + datasourcepane: true, + querypane: true, + }, + }, apiUrl: "/api/", baseUrl, logLevel: "info", - featureFlags: [ - FeatureFlagEnum.ApiPaneV2, - FeatureFlagEnum.DatasourcePane, - FeatureFlagEnum.QueryPane, - ], }); export default stageConfig; diff --git a/app/client/src/configs/types.ts b/app/client/src/configs/types.ts index 11c9975303..886c93ccce 100644 --- a/app/client/src/configs/types.ts +++ b/app/client/src/configs/types.ts @@ -1,5 +1,4 @@ import { LogLevelDesc } from "loglevel"; -import { FeatureFlagEnum } from "utils/featureFlags"; export type SentryConfig = { dsn: string; @@ -11,6 +10,24 @@ export type HotjarConfig = { sv: string; }; +type Milliseconds = number; + +export enum FeatureFlagsEnum { + ApiPaneV2 = "apipanev2", + DatasourcePane = "datasourcepane", + QueryPane = "querypane", + documentationV2 = "documentationv2", +} + +export type FeatureFlags = Record; + +export type FeatureFlagConfig = { + remoteConfig?: { + optimizely: string; + }; + default: FeatureFlags; +}; + export type AppsmithUIConfigs = { sentry: { enabled: boolean; @@ -20,6 +37,7 @@ export type AppsmithUIConfigs = { enabled: boolean; config?: HotjarConfig; }; + featureFlag: FeatureFlagConfig; segment: { enabled: boolean; key: string; @@ -27,5 +45,4 @@ export type AppsmithUIConfigs = { apiUrl: string; baseUrl: string; logLevel: LogLevelDesc; - featureFlags: Array; }; diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index bfd32a2ceb..ddf78e9d37 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -12,6 +12,8 @@ export const Colors: Record = { GRAY_CHATEAU: "#A2A6A8", LIGHT_GREYISH_BLUE: "#B0BFCB", SUNGLOW: "#FFCB33", + SOFT_ORANGE: "#f7c75b", + PURE_ORANGE: "#ffb100", BLACK: "#000000", BLACK_PEARL: "#040627", diff --git a/app/client/src/constants/DefaultTheme.tsx b/app/client/src/constants/DefaultTheme.tsx index 6a71b67b61..66f2758924 100644 --- a/app/client/src/constants/DefaultTheme.tsx +++ b/app/client/src/constants/DefaultTheme.tsx @@ -347,6 +347,9 @@ export const theme: Theme = { widgetSecondaryBorder: Colors.MERCURY, messageBG: Colors.CONCRETE, paneIcon: Colors.TROUT, + notification: Colors.JAFFA, + bindingTextDark: Colors.SOFT_ORANGE, + bindingText: Colors.PURE_ORANGE, }, lineHeights: [0, 14, 18, 22, 24, 28, 36, 48, 64, 80], fonts: [ diff --git a/app/client/src/constants/HelpConstants.ts b/app/client/src/constants/HelpConstants.ts new file mode 100644 index 0000000000..4cd9da45b0 --- /dev/null +++ b/app/client/src/constants/HelpConstants.ts @@ -0,0 +1,92 @@ +export const HelpMap = { + CONTAINER_WIDGET: { + path: "/widget-reference/container", + searchKey: "Container", + }, + CHECKBOX_WIDGET: { + path: "/widget-reference/checkbox", + searchKey: "Checkbox", + }, + BUTTON_WIDGET: { + path: "/widget-reference/button", + searchKey: "Button", + }, + TEXT_WIDGET: { + path: "/widget-reference/text", + searchKey: "Text", + }, + IMAGE_WIDGET: { + path: "/widget-reference/image", + searchKey: "Image", + }, + INPUT_WIDGET: { + path: "/widget-reference/input", + searchKey: "Input", + }, + DATE_PICKER_WIDGET: { + path: "/widget-reference/datepicker", + searchKey: "DatePicker", + }, + TABLE_WIDGET: { + path: "/widget-reference/table", + searchKey: "Table", + }, + DROP_DOWN_WIDGET: { + path: "/widget-reference/dropdown", + searchKey: "Dropdown", + }, + RADIO_GROUP_WIDGET: { + path: "/widget-reference/radio", + searchKey: "Radio", + }, + RICH_TEXT_EDITOR_WIDGET: { + path: "", + searchKey: "Rich Text Editor", + }, + CHART_WIDGET: { + path: "/widget-reference/chart", + searchKey: "Chart", + }, + FORM_WIDGET: { + path: "/widget-reference/form", + searchKey: "Form", + }, + FILE_PICKER_WIDGET: { + path: "/widget-reference/filepicker", + searchKey: "File picker", + }, + FORM_BUTTON_WIDGET: { + path: "", + searchKey: "", + }, + CANVAS_WIDGET: { + path: "", + searchKey: "", + }, + ICON_WIDGET: { + path: "", + searchKey: "", + }, + TABS_WIDGET: { + path: "", + searchKey: "Tabs", + }, + MODAL_WIDGET: { + path: "", + searchKey: "", + }, + MAP_WIDGET: { + path: "", + searchKey: "Map", + }, + API_MAIN: { + path: "/core-concepts/apis", + searchKey: "Apis", + }, + API_BINDING: { + path: "/core-concepts/apis/taking-inputs-from-widgets", + searchKey: "Taking Inputs from Widgets", + }, +}; + +export const HelpBaseURL = "https://docs.appsmith.com"; diff --git a/app/client/src/constants/Layers.tsx b/app/client/src/constants/Layers.tsx new file mode 100644 index 0000000000..123701c7a3 --- /dev/null +++ b/app/client/src/constants/Layers.tsx @@ -0,0 +1,25 @@ +import React from "react"; + +enum Indices { + Layer0, + Layer1, + Layer2, + Layer3, + Layer4, + Layer5, + Layer6, + Layer7, + LayerMax = 99999, +} + +export const Layers = { + dropZone: Indices.Layer0, + dragPreview: Indices.Layer1, + widgetName: Indices.Layer2, + apiPane: Indices.Layer3, + help: Indices.Layer4, + dynamicAutoComplete: Indices.Layer5, + max: Indices.LayerMax, +}; + +export const LayersContext = React.createContext(Layers); diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index 099d26176b..6e93b5070e 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -213,6 +213,8 @@ export const ReduxActionTypes: { [key: string]: string } = { FETCH_PROVIDER_DETAILS_BY_PROVIDER_ID_SUCCESS: "FETCH_PROVIDER_DETAILS_BY_PROVIDER_ID_SUCCESS", SET_PROVIDERS_LENGTH: "SET_PROVIDERS_LENGTH", + SET_DEFAULT_REFINEMENT: "SET_DEFAULT_REFINEMENT", + SET_HELP_MODAL_OPEN: "SET_HELP_MODAL_OPEN", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/constants/routes.ts b/app/client/src/constants/routes.ts index 42e187ed5e..c1567e24f5 100644 --- a/app/client/src/constants/routes.ts +++ b/app/client/src/constants/routes.ts @@ -1,5 +1,6 @@ import { MenuIcons } from "icons/MenuIcons"; -import { FeatureFlagEnum } from "utils/featureFlags"; +import FeatureFlag from "utils/featureFlags"; +import { FeatureFlagsEnum } from "configs/types"; export const BASE_URL = "/"; export const ORG_URL = "/org"; @@ -174,7 +175,7 @@ export const EDITOR_ROUTES = [ path: QUERIES_EDITOR_URL, title: "Queries", exact: false, - flag: FeatureFlagEnum.QueryPane, + allowed: FeatureFlag.check(FeatureFlagsEnum.QueryPane), }, { icon: MenuIcons.DATASOURCES_ICON, @@ -182,7 +183,7 @@ export const EDITOR_ROUTES = [ path: DATA_SOURCES_EDITOR_URL, title: "Datasources", exact: false, - flag: FeatureFlagEnum.DatasourcePane, + allowed: FeatureFlag.check(FeatureFlagsEnum.DatasourcePane), }, { icon: MenuIcons.PAGES_ICON, diff --git a/app/client/src/icons/ControlIcons.tsx b/app/client/src/icons/ControlIcons.tsx index 6acd0f9e95..de7d527747 100644 --- a/app/client/src/icons/ControlIcons.tsx +++ b/app/client/src/icons/ControlIcons.tsx @@ -12,6 +12,8 @@ import { ReactComponent as DecreaseIcon } from "assets/icons/control/decrease.sv import { ReactComponent as DraggableIcon } from "assets/icons/control/draggable.svg"; import { ReactComponent as CloseIcon } from "assets/icons/control/close.svg"; +import { ReactComponent as HelpIcon } from "assets/icons/control/help.svg"; + import { ReactComponent as PickMyLocationSelectedIcon } from "assets/icons/control/pick-location-selected.svg"; /* eslint-disable react/display-name */ @@ -78,6 +80,11 @@ export const ControlIcons: { ), + HELP_CONTROL: (props: IconProps) => ( + + + + ), }; export type ControlIconName = keyof typeof ControlIcons; diff --git a/app/client/src/icons/HelpIcons.tsx b/app/client/src/icons/HelpIcons.tsx new file mode 100644 index 0000000000..bfa162cb4f --- /dev/null +++ b/app/client/src/icons/HelpIcons.tsx @@ -0,0 +1,17 @@ +import React, { JSXElementConstructor } from "react"; +import { IconProps, IconWrapper } from "constants/IconConstants"; +import { ReactComponent as OpenLinkIcon } from "assets/icons/help/openlink.svg"; + +/* eslint-disable react/display-name */ + +export const HelpIcons: { + [id: string]: JSXElementConstructor; +} = { + OPEN_LINK: (props: IconProps) => ( + + + + ), +}; + +export type HelpIconName = keyof typeof HelpIcons; diff --git a/app/client/src/icons/MenuIcons.tsx b/app/client/src/icons/MenuIcons.tsx index c7a2b86318..db176d88ed 100644 --- a/app/client/src/icons/MenuIcons.tsx +++ b/app/client/src/icons/MenuIcons.tsx @@ -7,6 +7,7 @@ import { ReactComponent as PagesIcon } from "assets/icons/menu/pages.svg"; import { ReactComponent as DataSourcesIcon } from "assets/icons/menu/data-sources.svg"; import { ReactComponent as QueriesIcon } from "assets/icons/menu/queries.svg"; import { ReactComponent as HomepageIcon } from "assets/icons/menu/homepage.svg"; +import { Icon } from "@blueprintjs/core"; /* eslint-disable react/display-name */ export const MenuIcons: { @@ -47,4 +48,9 @@ export const MenuIcons: { ), + DOCS_ICON: (props: IconProps) => ( + + + + ), }; diff --git a/app/client/src/index.tsx b/app/client/src/index.tsx index 7474c9311a..4152fbc0c1 100755 --- a/app/client/src/index.tsx +++ b/app/client/src/index.tsx @@ -24,6 +24,8 @@ import { BASE_SIGNUP_URL, USERS_URL, } from "constants/routes"; +import { HelpModal } from "components/designSystems/appsmith/help/HelpModal"; +import { LayersContext, Layers } from "constants/Layers"; const loadingIndicator = ; const App = lazy(() => @@ -56,73 +58,75 @@ appInitializer(); ReactDOM.render( - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + , document.getElementById("root"), ); diff --git a/app/client/src/pages/Editor/APIEditor/Form.tsx b/app/client/src/pages/Editor/APIEditor/Form.tsx index b29a2f220d..0b2639df6d 100644 --- a/app/client/src/pages/Editor/APIEditor/Form.tsx +++ b/app/client/src/pages/Editor/APIEditor/Form.tsx @@ -29,6 +29,9 @@ import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScre import { FormIcons } from "icons/FormIcons"; import { BaseTabbedView } from "components/designSystems/appsmith/TabbedView"; import Pagination, { PaginationType } from "./Pagination"; +import { Icon } from "@blueprintjs/core"; +import { HelpMap, HelpBaseURL } from "constants/HelpConstants"; +import CollapsibleHelp from "components/designSystems/appsmith/help/CollapsibleHelp"; const Form = styled.form` display: flex; @@ -99,6 +102,18 @@ const TabbedViewContainer = styled.div` padding-top: 12px; `; +export const BindingText = styled.span` + color: ${props => props.theme.colors.bindingTextDark}; + font-weight: 700; +`; + +const StyledOpenDocsIcon = styled(Icon)` + svg { + width: 12px; + height: 18px; + } +`; + interface APIFormProps { pluginId: string; allowSave: boolean; @@ -229,6 +244,17 @@ const ApiEditorForm: React.FC = (props: Props) => { title: "API Input", panelComponent: ( + + {`Having trouble taking inputs from widget?`} + + {" Learn How "} + + + { showLineNumbers allowTabIndent singleLine={false} + placeholder={ + 'Please enter this POST request\'s JSON body.\n\n\nDid you know?\n\tIn Appsmith, we can use a widget\'s or API\'s property dynamically, using {{ }} templates.\n\n\tFor example: If we have an input widget named Input1 in which the user would provide their name \n\tand this post body structure should be { "name": "" } \n\tWe can access it in this post body using { "name": "{{Input1.text}}" }' + } /> diff --git a/app/client/src/pages/Editor/APIEditor/index.tsx b/app/client/src/pages/Editor/APIEditor/index.tsx index 79876e1181..37274420d2 100644 --- a/app/client/src/pages/Editor/APIEditor/index.tsx +++ b/app/client/src/pages/Editor/APIEditor/index.tsx @@ -25,8 +25,9 @@ import { UserApplication } from "constants/userConstants"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { getActionById, getCurrentPageName } from "selectors/editorSelectors"; import { Plugin } from "api/PluginApi"; -import { checkForFlag, FeatureFlagEnum } from "utils/featureFlags"; import styled from "styled-components"; +import FeatureFlag from "utils/featureFlags"; +import { FeatureFlagsEnum } from "configs/types"; const EmptyStateContainer = styled.div` display: flex; @@ -163,10 +164,14 @@ class ApiEditor extends React.Component { {"Create / Select an API from the list"} ); - const v2Flag = checkForFlag(FeatureFlagEnum.ApiPaneV2); + const v2Flag = FeatureFlag.check(FeatureFlagsEnum.ApiPaneV2); const homeScreen = v2Flag ? apiHomeScreen : defaultHomeScreen; return ( - +
{apiId ? ( <> {formUiComponent === "ApiEditorForm" && ( @@ -213,7 +218,7 @@ class ApiEditor extends React.Component { ) : ( homeScreen )} - +
); } } diff --git a/app/client/src/pages/Editor/ApiSidebar.tsx b/app/client/src/pages/Editor/ApiSidebar.tsx index 69c21d2e10..e80690968a 100644 --- a/app/client/src/pages/Editor/ApiSidebar.tsx +++ b/app/client/src/pages/Editor/ApiSidebar.tsx @@ -24,7 +24,8 @@ import EditorSidebar from "pages/Editor/EditorSidebar"; import { getNextEntityName } from "utils/AppsmithUtils"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { Page } from "constants/ReduxActionConstants"; -import { checkForFlag, FeatureFlagEnum } from "utils/featureFlags"; +import { FeatureFlagsEnum } from "configs/types"; +import FeatureFlag from "utils/featureFlags"; const HTTPMethod = styled.span<{ method?: string }>` flex: 1; @@ -185,7 +186,7 @@ class ApiSidebar extends React.Component { handleCreateNewApiClick = (selectedPageId: string) => { const { history, createNewApiAction } = this.props; const { pageId, applicationId } = this.props.match.params; - const v2Flag = checkForFlag(FeatureFlagEnum.ApiPaneV2); + const v2Flag = FeatureFlag.check(FeatureFlagsEnum.ApiPaneV2); if (v2Flag) { history.push( API_EDITOR_URL_WITH_SELECTED_PAGE_ID( diff --git a/app/client/src/pages/Editor/EditorHeader.tsx b/app/client/src/pages/Editor/EditorHeader.tsx index f41c037de8..3ecdbb026d 100644 --- a/app/client/src/pages/Editor/EditorHeader.tsx +++ b/app/client/src/pages/Editor/EditorHeader.tsx @@ -20,6 +20,9 @@ import { getOnSelectAction, } from "pages/common/CustomizedDropdown/dropdownHelpers"; import AnalyticsUtil from "utils/AnalyticsUtil"; +import { HelpModal } from "components/designSystems/appsmith/help/HelpModal"; +import FeatureFlag from "utils/featureFlags"; +import { FeatureFlagsEnum } from "configs/types"; const LoadingContainer = styled.div` display: flex; @@ -137,7 +140,6 @@ export const EditorHeader = (props: EditorHeaderProps) => { - {saveStatusMessage}
+ } + position={Position.TOP} + hoverOpenDelay={200} + > + + { diff --git a/app/client/src/pages/Editor/Sidebar.tsx b/app/client/src/pages/Editor/Sidebar.tsx index 4edfb7e2b0..8ec25e01ed 100644 --- a/app/client/src/pages/Editor/Sidebar.tsx +++ b/app/client/src/pages/Editor/Sidebar.tsx @@ -1,10 +1,9 @@ -import React from "react"; +import React, { useState } from "react"; import { useParams } from "react-router-dom"; import styled from "styled-components"; import SidebarComponent from "components/editorComponents/Sidebar"; import NavBarItem from "components/editorComponents/NavBarItem"; import { EDITOR_ROUTES, BuilderRouteParams } from "constants/routes"; -import { checkForFlag } from "utils/featureFlags"; const Wrapper = styled.div` display: grid; @@ -17,6 +16,8 @@ const Wrapper = styled.div` const NavBar = styled.div` background-color: ${props => props.theme.colors.navBG}; color: ${props => props.theme.colors.textOnDarkBG}; + display: flex; + flex-direction: column; `; const EditorSidebar = styled(SidebarComponent)` @@ -24,22 +25,60 @@ const EditorSidebar = styled(SidebarComponent)` `; const allowedRoutes = EDITOR_ROUTES.filter(route => { - if (route.flag) return checkForFlag(route.flag); - return true; + return route.allowed !== undefined ? route.allowed : true; }); +const TopBar = styled.div` + border-bottom: 1px solid #4e5d78; +`; +const BottomBar = styled.div` + display: flex; + padding-bottom: 118px; + height: 100%; + align-items: flex-end; +`; + const Sidebar = () => { const params = useParams(); + // const docsNavConfig = { + // icon: MenuIcons.DOCS_ICON, + // className: "t--nav-link-docs", + // title: "Docs", + // exact: true, + // }; + // const [highlightDocs, setHighlightDocs] = useState( + // !localStorage.getItem("hasSeenDocs"), + // ); + // const [hasSeenDocs, setHasSeenDocs] = useLocalStorage("hasSeenDocs", false); return ( - {allowedRoutes.map(config => ( - - ))} + + {allowedRoutes.map(config => ( + + ))} + + + {/* { + setHighlightDocs(false); + localStorage.setItem("hasSeenDocs", "true"); + }} + {...docsNavConfig} + external + highlight={highlightDocs} + path={HelpBaseURL} + > */} + diff --git a/app/client/src/reducers/index.tsx b/app/client/src/reducers/index.tsx index 0d469e68c8..290caf5753 100644 --- a/app/client/src/reducers/index.tsx +++ b/app/client/src/reducers/index.tsx @@ -27,6 +27,7 @@ import { ImportedCollectionsReduxState } from "reducers/uiReducers/importedColle import { ProvidersReduxState } from "reducers/uiReducers/providerReducer"; import { MetaState } from "./entityReducers/metaReducer"; import { ImportReduxState } from "reducers/uiReducers/importReducer"; +import { HelpReduxState } from "./uiReducers/helpReducer"; const appReducer = combineReducers({ entities: entityReducer, @@ -54,6 +55,7 @@ export interface AppState { imports: ImportReduxState; queryPane: QueryPaneReduxState; datasourcePane: DatasourcePaneReduxState; + help: HelpReduxState; }; entities: { canvasWidgets: CanvasWidgetsReduxState; diff --git a/app/client/src/reducers/uiReducers/helpReducer.ts b/app/client/src/reducers/uiReducers/helpReducer.ts new file mode 100644 index 0000000000..6209187abc --- /dev/null +++ b/app/client/src/reducers/uiReducers/helpReducer.ts @@ -0,0 +1,35 @@ +import { createReducer } from "utils/AppsmithUtils"; +import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; + +const initialState: HelpReduxState = { + url: "", + modalOpen: false, + height: 442, + width: 352, + defaultRefinement: "", +}; + +const helpReducer = createReducer(initialState, { + [ReduxActionTypes.SET_DEFAULT_REFINEMENT]: ( + state: HelpReduxState, + action: ReduxAction, + ) => { + return { ...state, defaultRefinement: action.payload }; + }, + [ReduxActionTypes.SET_HELP_MODAL_OPEN]: ( + state: HelpReduxState, + action: ReduxAction, + ) => { + return { ...state, modalOpen: action.payload }; + }, +}); + +export interface HelpReduxState { + url: string; + modalOpen: boolean; + height: number; + width: number; + defaultRefinement: string; +} + +export default helpReducer; diff --git a/app/client/src/reducers/uiReducers/index.tsx b/app/client/src/reducers/uiReducers/index.tsx index 0def9554a4..8da862f367 100644 --- a/app/client/src/reducers/uiReducers/index.tsx +++ b/app/client/src/reducers/uiReducers/index.tsx @@ -15,6 +15,7 @@ import importedCollectionsReducer from "./importedCollectionsReducer"; import providersReducer from "./providerReducer"; import importReducer from "./importReducer"; import queryPaneReducer from "./queryPaneReducer"; +import helpReducer from "./helpReducer"; const uiReducer = combineReducers({ widgetSidebar: widgetSidebarReducer, @@ -33,5 +34,6 @@ const uiReducer = combineReducers({ imports: importReducer, queryPane: queryPaneReducer, datasourcePane: datasourcePaneReducer, + help: helpReducer, }); export default uiReducer; diff --git a/app/client/src/selectors/helpSelectors.tsx b/app/client/src/selectors/helpSelectors.tsx new file mode 100644 index 0000000000..b9e04e752f --- /dev/null +++ b/app/client/src/selectors/helpSelectors.tsx @@ -0,0 +1,18 @@ +import { AppState } from "reducers"; + +export const getHelpUrl = (state: AppState): string => state.ui.help.url; +export const getHelpModalOpen = (state: AppState): boolean => + state.ui.help.modalOpen; + +export const getHelpModalDimensions = ( + state: AppState, +): { height: number; width: number } => { + return { + height: state.ui.help.height, + width: state.ui.help.width, + }; +}; + +export const getDefaultRefinement = (state: AppState): string => { + return state.ui.help.defaultRefinement || ""; +}; diff --git a/app/client/src/utils/AnalyticsUtil.tsx b/app/client/src/utils/AnalyticsUtil.tsx index 6ff98377a8..5b61253ee0 100644 --- a/app/client/src/utils/AnalyticsUtil.tsx +++ b/app/client/src/utils/AnalyticsUtil.tsx @@ -1,5 +1,6 @@ // Events import * as log from "loglevel"; +import FeatureFlag from "./featureFlags"; export type EventName = | "LOGIN_CLICK" @@ -191,6 +192,7 @@ class AnalyticsUtil { static identifyUser(userId: string, userData: User) { const windowDoc: any = window; AnalyticsUtil.user = userData; + FeatureFlag.identify(userData); if (windowDoc.analytics) { windowDoc.analytics.identify(userId, userData); } diff --git a/app/client/src/utils/AppsmithUtils.tsx b/app/client/src/utils/AppsmithUtils.tsx index bb29260aa8..56cb81ff15 100644 --- a/app/client/src/utils/AppsmithUtils.tsx +++ b/app/client/src/utils/AppsmithUtils.tsx @@ -10,7 +10,7 @@ import { ActionDataState } from "reducers/entityReducers/actionsReducer"; import * as log from "loglevel"; import { LogLevelDesc } from "loglevel"; import { providerBackgroundColors } from "constants/providerConstants"; -import { FeatureFlagEnum } from "utils/featureFlags"; +import FeatureFlag from "utils/featureFlags"; export const createReducer = ( initialState: any, @@ -28,6 +28,8 @@ export const createReducer = ( export const appInitializer = () => { FormControlRegistry.registerFormControlBuilders(); const appsmithConfigs = getAppsmithConfigs(); + FeatureFlag.initialize(appsmithConfigs.featureFlag); + if (appsmithConfigs.sentry.enabled && appsmithConfigs.sentry.config) { Sentry.init(appsmithConfigs.sentry.config); } @@ -38,8 +40,9 @@ export const appInitializer = () => { if (appsmithConfigs.segment.enabled) { AnalyticsUtil.initializeSegment(appsmithConfigs.segment.key); } + log.setLevel(getEnvLogLevel(appsmithConfigs.logLevel)); - setConfigFeatureFlags(appsmithConfigs.featureFlags); + // setConfigFeatureFlags(appsmithConfigs.featureFlags); const textFont = new FontFaceObserver("DM Sans"); textFont @@ -133,11 +136,11 @@ const getEnvLogLevel = (configLevel: LogLevelDesc): LogLevelDesc => { return logLevel; }; -const setConfigFeatureFlags = (flags: Array) => { - flags.forEach(flag => { - localStorage.setItem(flag, "true"); - }); -}; +// const setConfigFeatureFlags = (flags: Array) => { +// flags.forEach(flag => { +// localStorage.setItem(flag, "true"); +// }); +// }; export const getInitialsAndColorCode = (fullName: any): string[] => { let inits = ""; @@ -166,3 +169,24 @@ const getColorCode = (initials: string): string => { } return providerBackgroundColors[asciiSum % providerBackgroundColors.length]; }; + +export function hexToRgb( + hex: string, +): { + r: number; + g: number; + b: number; +} { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : { + r: -1, + g: -1, + b: -1, + }; +} diff --git a/app/client/src/utils/featureFlags.ts b/app/client/src/utils/featureFlags.ts index 915c1c6c6c..42d859854e 100644 --- a/app/client/src/utils/featureFlags.ts +++ b/app/client/src/utils/featureFlags.ts @@ -1,9 +1,99 @@ -export enum FeatureFlagEnum { - ApiPaneV2 = "ApiPaneV2", - DatasourcePane = "DatasourcePane", - QueryPane = "QueryPane", +import { FeatureFlagConfig, FeatureFlagsEnum } from "configs/types"; +const optimizelySDK = require("@optimizely/optimizely-sdk"); + +class FeatureFlag { + static isInitialized = false; + static remote = undefined; + static initialize(featureFlagConfig: FeatureFlagConfig) { + Object.keys(featureFlagConfig.default).forEach((flag: any) => { + // This is required because otherwise it will reset the values + // every time the application is loaded. We need the application to load + // remote values the second time. + if (localStorage.getItem(flag) === null) { + localStorage.setItem( + flag, + featureFlagConfig.default[flag as FeatureFlagsEnum].toString(), + ); + } + }); + + if (featureFlagConfig.remoteConfig) { + FeatureFlag.remote = optimizelySDK.createInstance({ + sdkKey: featureFlagConfig.remoteConfig.optimizely, + }); + (FeatureFlag.remote as any).onReady().then(onInit); + } + } + + static identify(userData: any) { + if (FeatureFlag.remote) { + if (FeatureFlag.isInitialized) { + updateFlagsInLocalStorage(userData); + } else { + (FeatureFlag.remote as any).onReady().then(() => { + onInit(); + updateFlagsInLocalStorage(userData); + }); + } + } + } + + static check(flagName: string) { + return localStorage.getItem(flagName) === "true"; + } } -export const checkForFlag = (flagName: FeatureFlagEnum) => { - return localStorage.getItem(flagName); -}; +function onInit() { + FeatureFlag.isInitialized = true; +} + +function updateFlagsInLocalStorage(userData: any) { + const userId = userData.id; + const email = userData.email; + const optimizelyClientInstance = FeatureFlag.remote as any; + localStorage.setItem( + FeatureFlagsEnum.DatasourcePane, + optimizelyClientInstance.isFeatureEnabled( + FeatureFlagsEnum.DatasourcePane, + userId, + { + email: email, + }, + ), + ); + + localStorage.setItem( + FeatureFlagsEnum.ApiPaneV2, + optimizelyClientInstance.isFeatureEnabled( + FeatureFlagsEnum.ApiPaneV2, + userId, + { + email: email, + }, + ), + ); + + localStorage.setItem( + FeatureFlagsEnum.documentationV2, + optimizelyClientInstance.isFeatureEnabled( + FeatureFlagsEnum.documentationV2, + userId, + { + email: email, + }, + ), + ); + + localStorage.setItem( + FeatureFlagsEnum.QueryPane, + optimizelyClientInstance.isFeatureEnabled( + FeatureFlagsEnum.QueryPane, + userId, + { + email: email, + }, + ), + ); +} + +export default FeatureFlag; diff --git a/app/client/yarn.lock b/app/client/yarn.lock index 0d292edf0b..ba97a5ec06 100644 --- a/app/client/yarn.lock +++ b/app/client/yarn.lock @@ -2,6 +2,110 @@ # yarn lockfile v1 +"@algolia/cache-browser-local-storage@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.2.0.tgz#45cc4be4c8fcd69cb98ebaa2e78a459a1cf6ba64" + integrity sha512-uji5zxBxwNu8qKtyqghg9lUsN0OOZ58NfRKk0Il4IZCcCo78E0KfT3Uxr7XiYCJMRnqIsvbKWf0xA67tYNBSbA== + dependencies: + "@algolia/cache-common" "4.2.0" + +"@algolia/cache-common@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.2.0.tgz#ada18e559f205a63eaf60c21a035b3d41f0f8d7d" + integrity sha512-ATBQCBBLt4hPNKIKn06y5zqZPWQmI+PBF0287rFVj8BGmEr82BzoKMa5XIkvgpjtxwx6c5nSKxZaYkEFqtrxtQ== + +"@algolia/cache-in-memory@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.2.0.tgz#82f07cc99aee9e20a96bdd69c635bdd2dc4288f1" + integrity sha512-NsVOR6ixK6jvurLW+1+h80/9N18QjU/AXdAZJoVeu4JXb2NPuej4Ld1zXFYvz/ypCFQE+dU8haaQnJIuTbD4vg== + dependencies: + "@algolia/cache-common" "4.2.0" + +"@algolia/client-account@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.2.0.tgz#7abf3dd8922fde1735b1e0d19e8b0bdbf64a1435" + integrity sha512-xz5OXU9DQ9pegABAgmTPV23f9tXmbUPO3w5J/b2QcP6jzfNnNfW3CkTwywgNLr16jIKLxmmClN5yqyJp6XmHBA== + dependencies: + "@algolia/client-common" "4.2.0" + "@algolia/client-search" "4.2.0" + "@algolia/transporter" "4.2.0" + +"@algolia/client-analytics@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.2.0.tgz#11e9331fed5bbaa6668d71c890dff60c4af1c741" + integrity sha512-UNuZQOYuKPYl5fCgm1HZzoZ6Ewxtqrc4Cv5Dhdy5VatIV6lYEWOtdn+g+5qvWFGb6fv6688dg5EVJnXZNvVVZQ== + dependencies: + "@algolia/client-common" "4.2.0" + "@algolia/client-search" "4.2.0" + "@algolia/requester-common" "4.2.0" + "@algolia/transporter" "4.2.0" + +"@algolia/client-common@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.2.0.tgz#bf8a550dc51927bb103de9aab7e6ac4d90a9cf0d" + integrity sha512-KxZTWXf9FSl188iTAz9rhTMeBtbF/uaJcxw99jbWHxyK9KR87obZzTlTFYnIWLEBaTG1MmlgPSsDogAE4CHLOQ== + dependencies: + "@algolia/requester-common" "4.2.0" + "@algolia/transporter" "4.2.0" + +"@algolia/client-recommendation@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/client-recommendation/-/client-recommendation-4.2.0.tgz#bd49b5b9601fe4220ba6db0fc397d816584ee4ec" + integrity sha512-5QwvUJ5hpZVDz99o+EPgMg+z7maLWOZGUrUt5z8s+esl+taTb2h1PtyLpikAvC2d/BjYCEKyObTiRDYdzhqcoA== + dependencies: + "@algolia/client-common" "4.2.0" + "@algolia/requester-common" "4.2.0" + "@algolia/transporter" "4.2.0" + +"@algolia/client-search@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.2.0.tgz#4917499cac66a5cca7f2ca9d1334bffc96a79b17" + integrity sha512-2SAz1/undr+RM7FNj3G0taWFG+8QEMQcYHxUhoOJKMIY9sPQN7UNCJRHYsulM+/g45oF67tXX09NSt14ewen0Q== + dependencies: + "@algolia/client-common" "4.2.0" + "@algolia/requester-common" "4.2.0" + "@algolia/transporter" "4.2.0" + +"@algolia/logger-common@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.2.0.tgz#dd373b267594656d72a1563f6621ab7f727c4373" + integrity sha512-VQcJE5lr78oc+lbcGfPonCDTRwLNSxwtPrUP6Tj+CoDedsVHZhODAlHzLHhxc4vuyrU7xomvKJLqTUgfDNxzXQ== + +"@algolia/logger-console@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.2.0.tgz#10e18ab75f60fd0f2e2b16cb9a1b0bcc947087f2" + integrity sha512-/1GE43jY0xKfJUi5ZGtEqq+oTyOzs+EgGKj7/zEHIpUc5NyxokIPWTqt3q6pzGSWFEkNbaA1gAVgXM1zCMVWYw== + dependencies: + "@algolia/logger-common" "4.2.0" + +"@algolia/requester-browser-xhr@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.2.0.tgz#c2a7982bef940e1749f2ba2aa04e3f8a971b6a78" + integrity sha512-+PZKOe+UBdZYQg/h/8AbKQ2Ha4uDeoLnpZFv00IMr/elym0m2hl76xAeIBiIqGYsLCmGybGBFUF9n1imsKJUJQ== + dependencies: + "@algolia/requester-common" "4.2.0" + +"@algolia/requester-common@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.2.0.tgz#df67a940516d5a313bbf79bcbceddadfff9f8ce2" + integrity sha512-SSKPRM/7UP54/dxyK6EYt4p6nTeJxYb1P6xVh/Ic6noBTCfqg5vBEKDa1DZD5MBtCvABoODd97UOfAo3ECG/jg== + +"@algolia/requester-node-http@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.2.0.tgz#e26efd3d630b7c988bcc9cda3a8ee68ab4a168dd" + integrity sha512-mRQgSM8qrMfjXaBnMjTmymR0NKwbr82Qwh1a5TgYyzMOBuRO5nRikawvTVgpNaEnQS0uesIiwd2ohOJ2gNu6oA== + dependencies: + "@algolia/requester-common" "4.2.0" + +"@algolia/transporter@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.2.0.tgz#9e6bea3304f1e6f4a64a3d7c1f9de047ba89056f" + integrity sha512-7CiwMYsEhrHySA8q70euIYOyhGtz/wz+MEC3nwGONBC82nGI6ntVqTFhCkpLIJqqbGbNlFgnCpwnLmSqLhRP3A== + dependencies: + "@algolia/cache-common" "4.2.0" + "@algolia/logger-common" "4.2.0" + "@algolia/requester-common" "4.2.0" + "@babel/code-frame@7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" @@ -1717,6 +1821,57 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@optimizely/js-sdk-datafile-manager@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@optimizely/js-sdk-datafile-manager/-/js-sdk-datafile-manager-0.5.0.tgz#d6283ca3f8d2a60f30a6c300aaadf6a99cd9255e" + integrity sha512-ZDov8jphA+Ez+fA0anioA8ooJrraCFbeGm7GejzPTCZPMisNGtchrpdHr9TMd+hld+TOMBZ5NbqfMvvYhbB34A== + dependencies: + "@optimizely/js-sdk-logging" "^0.1.0" + "@optimizely/js-sdk-utils" "^0.2.0" + decompress-response "^4.2.1" + +"@optimizely/js-sdk-event-processor@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@optimizely/js-sdk-event-processor/-/js-sdk-event-processor-0.4.0.tgz#51e06cfda2eedf6a7f3829f8e0132fb5c0b640a8" + integrity sha512-5fqBG9N66O+9KWktUTH/OmMiQ4SKi42gP7qqWNKe0Ciu5PlBMTREKmo8+EixcDvDW8yQBvIPBj6GWzKz0RVAxg== + dependencies: + "@optimizely/js-sdk-logging" "^0.1.0" + "@optimizely/js-sdk-utils" "^0.2.0" + +"@optimizely/js-sdk-logging@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@optimizely/js-sdk-logging/-/js-sdk-logging-0.1.0.tgz#e5950c3d9a708fbd5931a043130469c5df7f64e8" + integrity sha512-Bs2zHvsdNIk2QSg05P6mKIlROHoBIRNStbrVwlePm603CucojKRPlFJG4rt7sFZQOo8xS8I7z1BmE4QI3/ZE9A== + dependencies: + "@optimizely/js-sdk-utils" "^0.1.0" + +"@optimizely/js-sdk-utils@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@optimizely/js-sdk-utils/-/js-sdk-utils-0.1.0.tgz#e3ac1fef81f11c15774f4743c3fa7c65d9c3352a" + integrity sha512-p7499GgVaX94YmkrwOiEtLgxgjXTPbUQsvETaAil5J7zg1TOA4Wl8ClalLSvCh+AKWkxGdkL4/uM/zfbxPSNNw== + dependencies: + uuid "^3.3.2" + +"@optimizely/js-sdk-utils@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@optimizely/js-sdk-utils/-/js-sdk-utils-0.2.0.tgz#f24c971f09c3c63f7a4b9eb4da6ba9642bf61cd0" + integrity sha512-aHEccRVc5YjWAdIVtniKfUE3tuzHriIWZTS4sLEq/lXkNTITSL1jrBEJD91CVY5BahWu/aG/aOafrA7XGH3sDQ== + dependencies: + uuid "^3.3.2" + +"@optimizely/optimizely-sdk@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@optimizely/optimizely-sdk/-/optimizely-sdk-4.0.0.tgz#0e63fb3fdd70e95481029025b2c633e9bd93f88d" + integrity sha512-ufwndTjg6wPXnJmbW/3SK2F3Dt7E1S1VQZ5oCoYrsLZ2oFrhES/urbWWTzC1t83gAokbqzSEZDuc/OBdZ6c9SA== + dependencies: + "@optimizely/js-sdk-datafile-manager" "^0.5.0" + "@optimizely/js-sdk-event-processor" "^0.4.0" + "@optimizely/js-sdk-logging" "^0.1.0" + "@optimizely/js-sdk-utils" "^0.2.0" + json-schema "^0.2.3" + murmurhash "0.0.2" + uuid "^3.3.2" + "@reach/router@^1.2.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@reach/router/-/router-1.3.1.tgz#0a49f75fa9621323d6e21c803447bcfcde1713b2" @@ -2531,6 +2686,18 @@ dependencies: prop-types "^15.6.2" +"@types/algoliasearch-helper@*": + version "2.26.1" + resolved "https://registry.yarnpkg.com/@types/algoliasearch-helper/-/algoliasearch-helper-2.26.1.tgz#60cf377e7cb4bd9a55f7eba35182792763230a24" + integrity sha512-JN1wq/yLxxBcc6MeSe57F9Aqv8wL964L0nBOUTSQ5OECzWxaECuGYV06VnGKn/c+9AGB97RAgqx2PUbYflZNqA== + dependencies: + "@types/algoliasearch" "*" + +"@types/algoliasearch@*": + version "3.34.10" + resolved "https://registry.yarnpkg.com/@types/algoliasearch/-/algoliasearch-3.34.10.tgz#08cd8f7018451197d7a51c21e1305794aca187b9" + integrity sha512-P6OKrRClvlVIF645zmRdGt3pK/IlBeFMAqB/OqM5Z/iYVFp8mSYMFR+uAdADsv0X/8AC6bIPAfj2x+mjslBuFg== + "@types/anymatch@*": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" @@ -2751,6 +2918,22 @@ dependencies: "@types/react" "*" +"@types/react-instantsearch-core@*": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@types/react-instantsearch-core/-/react-instantsearch-core-6.3.0.tgz#74acc99250ce416a96a0b1896e127a283a9f6b76" + integrity sha512-EZVOXs9CBxTwGbIdYX/JUk2hB9/lkaGK0rSnbnJ9C1MXe73xEpV7THrlgaFg6BO+5yrEPAf2NazCALR1WpaVwA== + dependencies: + "@types/algoliasearch-helper" "*" + "@types/react" "*" + +"@types/react-instantsearch-dom@^6.3.0": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@types/react-instantsearch-dom/-/react-instantsearch-dom-6.3.0.tgz#5543a2fbe85d5c8a7658d61fdb92fcba1cf2e616" + integrity sha512-sq4WXWSVVhH8NV7s6xcc/c+SkiyMewRefCPleXK3p9676rIFyKwotRkh8x7IoXOBV3V2avfU3Xr65Y9scRZlZQ== + dependencies: + "@types/react" "*" + "@types/react-instantsearch-core" "*" + "@types/react-native@*": version "0.61.10" resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.61.10.tgz#d010b9093322bc781151638f74124f58fc87cc90" @@ -3394,6 +3577,33 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +algoliasearch-helper@^3.1.0, algoliasearch-helper@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.1.1.tgz#4cdfbaed6670d82626ac1fae001ccbf53192f497" + integrity sha512-Jkqlp8jezQRixf7sbQ2zFXHpdaT41g9sHBqT6pztv5nfDmg94K+pwesAy6UbxRY78IL54LIaV1FLttMtT+IzzA== + dependencies: + events "^1.1.1" + +algoliasearch@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.2.0.tgz#dd81a1a0c57eb9f74af6db70b0c11f256692d1e6" + integrity sha512-CgbyDBGMSzNISBFezPt68xAseknork+wNe/Oour1Hluk4OwbtobysRawFf93ZbLSQw/KbeGlVmVAvujeVIVdnQ== + dependencies: + "@algolia/cache-browser-local-storage" "4.2.0" + "@algolia/cache-common" "4.2.0" + "@algolia/cache-in-memory" "4.2.0" + "@algolia/client-account" "4.2.0" + "@algolia/client-analytics" "4.2.0" + "@algolia/client-common" "4.2.0" + "@algolia/client-recommendation" "4.2.0" + "@algolia/client-search" "4.2.0" + "@algolia/logger-common" "4.2.0" + "@algolia/logger-console" "4.2.0" + "@algolia/requester-browser-xhr" "4.2.0" + "@algolia/requester-common" "4.2.0" + "@algolia/requester-node-http" "4.2.0" + "@algolia/transporter" "4.2.0" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -5551,6 +5761,13 @@ decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" +decompress-response@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -6358,6 +6575,11 @@ eventemitter3@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" +events@^1.1.0, events@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= + events@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" @@ -7476,6 +7698,14 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +hogan.js@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd" + integrity sha1-TNnhq9QpQUbnZ55B14mHMrAse/0= + dependencies: + mkdirp "0.3.0" + nopt "1.0.10" + hoist-non-react-statics@^2.3.1: version "2.5.5" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" @@ -7832,6 +8062,24 @@ inquirer@7.0.4, inquirer@^7.0.0: strip-ansi "^5.1.0" through "^2.3.6" +instantsearch.css@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/instantsearch.css/-/instantsearch.css-7.4.2.tgz#2b02db07c865ce2c946ec20c1eb4556377cad90c" + integrity sha512-CaOdFWCpHOWGAkJRpwC4+8ROU3Upp3xrUuc0Mg92UucJ84MWMJWkVZDDyRwubgJe4HXQM2rV6b+bJaDeeZGunw== + +instantsearch.js@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/instantsearch.js/-/instantsearch.js-4.4.1.tgz#debbc77131bba35b6920fe0bfbcc65eb75484fd0" + integrity sha512-CphyvXNHYCkPYCUTPtjYA//AJ3yn29gX09KrcahxVfXbgxZDMUMcNnF0BnCAYjjVB9U49NXKOrZf6VZ8Z57EwA== + dependencies: + algoliasearch-helper "^3.1.1" + classnames "^2.2.5" + events "^1.1.0" + hogan.js "^3.0.2" + preact "^10.0.0" + prop-types "^15.5.10" + qs "^6.5.1" + internal-ip@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" @@ -8864,6 +9112,11 @@ json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" +json-schema@^0.2.3: + version "0.2.5" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.5.tgz#97997f50972dd0500214e208c407efa4b5d7063b" + integrity sha512-gWJOWYFrhQ8j7pVm0EM8Slr+EPVq1Phf6lvzvD/WCeqkrx/f2xBI0xOsRRS9xCn3I4vKtP519dvs3TP09r24wQ== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -9649,6 +9902,11 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + min-document@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" @@ -9756,6 +10014,11 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= + mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -9885,6 +10148,11 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" +murmurhash@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/murmurhash/-/murmurhash-0.0.2.tgz#6f07bd8a1105e709c26fc89420cb5930c24585fe" + integrity sha1-bwe9ihEF5wnCb8iUIMtZMMJFhf4= + mutationobserver-shim@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz#65869630bc89d7bf8c9cd9cb82188cd955aacd2b" @@ -10080,6 +10348,13 @@ node-sass@^4.11.0: stdout-stream "^1.4.0" "true-case-path" "^1.0.2" +nopt@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + "nopt@2 || 3": version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -11379,6 +11654,11 @@ preact@8.2.9: version "8.2.9" resolved "https://registry.yarnpkg.com/preact/-/preact-8.2.9.tgz#813ba9dd45e5d97c5ea0d6c86d375b3be711cc40" +preact@^10.0.0: + version "10.4.1" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.4.1.tgz#9b3ba020547673a231c6cf16f0fbaef0e8863431" + integrity sha512-WKrRpCSwL2t3tpOOGhf2WfTpcmbpxaWtDbdJdKdjd0aEiTkvOmS4NBkG6kzlaAHI9AkQ3iVqbFWM3Ei7mZ4o1Q== + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -11687,6 +11967,11 @@ qs@6.7.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" +qs@^6.5.1: + version "6.9.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== + qs@^6.6.0: version "6.9.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9" @@ -11988,6 +12273,11 @@ react-fast-compare@^2.0.2, react-fast-compare@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" +react-fast-compare@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.0.2.tgz#8b0bfa93a049ec1eef9d54ab5e5c6036c317144d" + integrity sha512-1fBw8Efm6fb1blbRorZcO3OqzWgxYWxXzlaNeqEVXcrDdyFzeqygrPVU6qrPuqnqYMpUJVkQBdDgDJQTmCXp+Q== + react-focus-lock@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.2.1.tgz#1d12887416925dc53481914b7cedd39494a3b24a" @@ -12053,6 +12343,27 @@ react-input-autosize@^2.2.2: dependencies: prop-types "^15.5.8" +react-instantsearch-core@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/react-instantsearch-core/-/react-instantsearch-core-6.4.0.tgz#12dd37163b3f3297d7ab6d1ee78b649df5e06d5f" + integrity sha512-iJ33uxpo9suc9p8LTH3v5by5BXzhuttBvSaoePZheuLLG8dnbpiUt+m0iC4oeyr1cgubKJ1OhGCL8AQN2fR11A== + dependencies: + "@babel/runtime" "^7.1.2" + algoliasearch-helper "^3.1.0" + prop-types "^15.5.10" + react-fast-compare "^3.0.0" + +react-instantsearch-dom@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/react-instantsearch-dom/-/react-instantsearch-dom-6.4.0.tgz#61c7e453652136ebc549e6427e2dab7d57519f07" + integrity sha512-FyJaD+jVNt59KOd8Nt6nQrmueEEJNH0VmS9IlSwWbQz2NrkQniaCFm+k8G13LsAASOCq4vQ8ULG2up+WpJeS8w== + dependencies: + "@babel/runtime" "^7.1.2" + algoliasearch-helper "^3.1.0" + classnames "^2.2.5" + prop-types "^15.5.10" + react-instantsearch-core "^6.4.0" + react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.12.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"