From 81ae3569f5317c913db9e42baf7031a8dfc783e0 Mon Sep 17 00:00:00 2001 From: Anemy Date: Fri, 19 Nov 2021 13:01:38 -0500 Subject: [PATCH 01/29] Add general tab fields, begin state management of form --- package-lock.json | 103 ++++++++++++++- packages/compass-components/package.json | 1 + .../src/components/accordion.tsx | 9 +- packages/compass-components/src/index.ts | 1 + .../src/advanced-options-tabs/general-tab.tsx | 7 -- .../advanced-connection-options.tsx | 0 .../advanced-options-tabs.tsx | 18 +-- .../advanced-options-tabs/advanced-tab.tsx | 0 .../advanced-options-tabs/general-tab.tsx | 119 ++++++++++++++++++ .../general/schema-input.tsx | 66 ++++++++++ .../advanced-options-tabs/ssh-tunnel-tab.tsx | 0 .../advanced-options-tabs/ssl-tab.tsx | 0 .../confirm-edit-connection-string.spec.tsx | 0 .../confirm-edit-connection-string.tsx | 0 .../src/components/connect-form-actions.tsx | 37 ++++++ .../{ => components}/connect-form.spec.tsx | 6 +- .../src/components/connect-form.tsx | 98 +++++++++++++++ .../connection-string-input.spec.tsx | 0 .../connection-string-input.tsx | 18 +-- packages/connect-form/src/connect-form.tsx | 96 -------------- .../src/contexts/connection-string-context.ts | 57 +++++++++ packages/connect-form/src/index.ts | 2 +- .../src/components/connections.tsx | 2 +- 23 files changed, 501 insertions(+), 139 deletions(-) delete mode 100644 packages/connect-form/src/advanced-options-tabs/general-tab.tsx rename packages/connect-form/src/{ => components}/advanced-connection-options.tsx (100%) rename packages/connect-form/src/{ => components}/advanced-options-tabs/advanced-options-tabs.tsx (75%) rename packages/connect-form/src/{ => components}/advanced-options-tabs/advanced-tab.tsx (100%) create mode 100644 packages/connect-form/src/components/advanced-options-tabs/general-tab.tsx create mode 100644 packages/connect-form/src/components/advanced-options-tabs/general/schema-input.tsx rename packages/connect-form/src/{ => components}/advanced-options-tabs/ssh-tunnel-tab.tsx (100%) rename packages/connect-form/src/{ => components}/advanced-options-tabs/ssl-tab.tsx (100%) rename packages/connect-form/src/{ => components}/confirm-edit-connection-string.spec.tsx (100%) rename packages/connect-form/src/{ => components}/confirm-edit-connection-string.tsx (100%) create mode 100644 packages/connect-form/src/components/connect-form-actions.tsx rename packages/connect-form/src/{ => components}/connect-form.spec.tsx (86%) create mode 100644 packages/connect-form/src/components/connect-form.tsx rename packages/connect-form/src/{ => components}/connection-string-input.spec.tsx (100%) rename packages/connect-form/src/{ => components}/connection-string-input.tsx (93%) delete mode 100644 packages/connect-form/src/connect-form.tsx create mode 100644 packages/connect-form/src/contexts/connection-string-context.ts diff --git a/package-lock.json b/package-lock.json index c6a86a48747..3cc090aed21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5447,6 +5447,55 @@ "react-dom": "^16.0.0" } }, + "node_modules/@leafygreen-ui/radio-box-group": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/radio-box-group/-/radio-box-group-6.1.4.tgz", + "integrity": "sha512-fB0LGbIFDUn1Xf2ybgRuM6yKKwHFaleiBHD2iijQddr/xyxqHmnZhpiT7Rmv1aP2jKPHDbYJjmg+PAx3s9k/NA==", + "dependencies": { + "@leafygreen-ui/hooks": "^7.0.0", + "@leafygreen-ui/interaction-ring": "^1.1.0", + "@leafygreen-ui/lib": "^9.0.0", + "@leafygreen-ui/palette": "^3.2.2" + }, + "peerDependencies": { + "@leafygreen-ui/leafygreen-provider": "^2.1.3" + } + }, + "node_modules/@leafygreen-ui/radio-box-group/node_modules/@leafygreen-ui/emotion": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-4.0.0.tgz", + "integrity": "sha512-nr2g6OFsy+psaMto3H4HQ1ivM1tCwd9k1bbR5WH4U7YibDagfBekFTwlhohmC/K7hUM/eDVPGw0w4zQyC+BwZg==", + "dependencies": { + "@emotion/css": "^11.1.3", + "@emotion/react": "^11.4.0", + "@emotion/server": "^11.4.0" + } + }, + "node_modules/@leafygreen-ui/radio-box-group/node_modules/@leafygreen-ui/lib": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-9.0.1.tgz", + "integrity": "sha512-VAGyjLzjYmvPGT/dhgr+VDk+wbL2DfMP6mfhrpgPhruV942xM3R8j0v92nsjhUZKUVIQR+wQiTDbjKfmd9teJQ==", + "dependencies": { + "@leafygreen-ui/emotion": "^4.0.0", + "facepaint": "^1.2.1", + "polished": "^4.1.3", + "prop-types": "^15.0.0" + }, + "peerDependencies": { + "react": "^17.0.0" + } + }, + "node_modules/@leafygreen-ui/radio-box-group/node_modules/polished": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.1.3.tgz", + "integrity": "sha512-ocPAcVBUOryJEKe0z2KLd1l9EBa1r5mSwlKpExmrLzsnIzJo4axsoU9O2BjOTkDGDT4mZ0WFE5XKTlR3nLnZOA==", + "dependencies": { + "@babel/runtime": "^7.14.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@leafygreen-ui/ripple": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@leafygreen-ui/ripple/-/ripple-1.1.1.tgz", @@ -63062,6 +63111,7 @@ "@leafygreen-ui/modal": "^7.0.0", "@leafygreen-ui/palette": "^3.2.2", "@leafygreen-ui/portal": "^3.1.3", + "@leafygreen-ui/radio-box-group": "^6.1.4", "@leafygreen-ui/select": "^3.0.4", "@leafygreen-ui/tabs": "^5.1.3", "@leafygreen-ui/text-area": "^4.0.3", @@ -98389,10 +98439,9 @@ "version": "0.1.0", "license": "SSPL", "dependencies": { - "@leafygreen-ui/tabs": "^5.1.5", "mongodb-connection-string-url": "^2.1.0", - "react": "^16.14.0", - "react-dom": "^16.14.0" + "react": "*", + "react-dom": "*" }, "devDependencies": { "@emotion/css": "^11.5.0", @@ -111805,6 +111854,48 @@ "@leafygreen-ui/lib": "^8.0.0" } }, + "@leafygreen-ui/radio-box-group": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/radio-box-group/-/radio-box-group-6.1.4.tgz", + "integrity": "sha512-fB0LGbIFDUn1Xf2ybgRuM6yKKwHFaleiBHD2iijQddr/xyxqHmnZhpiT7Rmv1aP2jKPHDbYJjmg+PAx3s9k/NA==", + "requires": { + "@leafygreen-ui/hooks": "^7.0.0", + "@leafygreen-ui/interaction-ring": "^1.1.0", + "@leafygreen-ui/lib": "^9.0.0", + "@leafygreen-ui/palette": "^3.2.2" + }, + "dependencies": { + "@leafygreen-ui/emotion": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/emotion/-/emotion-4.0.0.tgz", + "integrity": "sha512-nr2g6OFsy+psaMto3H4HQ1ivM1tCwd9k1bbR5WH4U7YibDagfBekFTwlhohmC/K7hUM/eDVPGw0w4zQyC+BwZg==", + "requires": { + "@emotion/css": "^11.1.3", + "@emotion/react": "^11.4.0", + "@emotion/server": "^11.4.0" + } + }, + "@leafygreen-ui/lib": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@leafygreen-ui/lib/-/lib-9.0.1.tgz", + "integrity": "sha512-VAGyjLzjYmvPGT/dhgr+VDk+wbL2DfMP6mfhrpgPhruV942xM3R8j0v92nsjhUZKUVIQR+wQiTDbjKfmd9teJQ==", + "requires": { + "@leafygreen-ui/emotion": "^4.0.0", + "facepaint": "^1.2.1", + "polished": "^4.1.3", + "prop-types": "^15.0.0" + } + }, + "polished": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.1.3.tgz", + "integrity": "sha512-ocPAcVBUOryJEKe0z2KLd1l9EBa1r5mSwlKpExmrLzsnIzJo4axsoU9O2BjOTkDGDT4mZ0WFE5XKTlR3nLnZOA==", + "requires": { + "@babel/runtime": "^7.14.0" + } + } + } + }, "@leafygreen-ui/ripple": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@leafygreen-ui/ripple/-/ripple-1.1.1.tgz", @@ -117605,6 +117696,7 @@ "@leafygreen-ui/modal": "^7.0.0", "@leafygreen-ui/palette": "^3.2.2", "@leafygreen-ui/portal": "^3.1.3", + "@leafygreen-ui/radio-box-group": "^6.1.4", "@leafygreen-ui/select": "^3.0.4", "@leafygreen-ui/tabs": "^5.1.3", "@leafygreen-ui/text-area": "^4.0.3", @@ -142246,7 +142338,6 @@ "version": "file:packages/connect-form", "requires": { "@emotion/css": "^11.5.0", - "@leafygreen-ui/tabs": "*", "@mongodb-js/compass-components": "^0.6.0", "@mongodb-js/eslint-config-compass": "^0.3.0", "@mongodb-js/mocha-config-compass": "^0.4.0", @@ -142268,8 +142359,8 @@ "mongodb-data-service": "^21.12.0", "nyc": "^15.1.0", "prettier": "2.3.2", - "react": "^16.14.0", - "react-dom": "^16.14.0", + "react": "*", + "react-dom": "*", "sinon": "^9.2.3", "xvfb-maybe": "^0.2.1" }, diff --git a/packages/compass-components/package.json b/packages/compass-components/package.json index 5f76c9e973e..816085f3899 100644 --- a/packages/compass-components/package.json +++ b/packages/compass-components/package.json @@ -48,6 +48,7 @@ "@leafygreen-ui/modal": "^7.0.0", "@leafygreen-ui/palette": "^3.2.2", "@leafygreen-ui/portal": "^3.1.3", + "@leafygreen-ui/radio-box-group": "^6.1.4", "@leafygreen-ui/select": "^3.0.4", "@leafygreen-ui/tabs": "^5.1.3", "@leafygreen-ui/text-area": "^4.0.3", diff --git a/packages/compass-components/src/components/accordion.tsx b/packages/compass-components/src/components/accordion.tsx index 4d5a68ed09b..a3cbe8b8eb4 100644 --- a/packages/compass-components/src/components/accordion.tsx +++ b/packages/compass-components/src/components/accordion.tsx @@ -21,15 +21,12 @@ const buttonStyles = css({ transition: 'box-shadow 150ms ease-in-out', '&:focus-visible': { boxShadow: `0 0 0 3px ${uiColors.focus}`, - }, + } }); const containerStyles = css({ marginTop: spacing[3], display: 'flex', - alignItems: 'center', - '&:hover': { - cursor: 'pointer', - }, + alignItems: 'center' }); interface AccordionProps { dataTestId?: string; @@ -55,7 +52,7 @@ function Accordion( }} > - {props.text} +  {props.text}

diff --git a/packages/compass-components/src/index.ts b/packages/compass-components/src/index.ts index a1a5f575f38..4ccdcfc1fe1 100644 --- a/packages/compass-components/src/index.ts +++ b/packages/compass-components/src/index.ts @@ -32,6 +32,7 @@ export { default as Modal } from '@leafygreen-ui/modal'; export { uiColors } from '@leafygreen-ui/palette'; export * as compassUIColors from './components/compass-ui-colors'; export { default as Portal } from '@leafygreen-ui/portal'; +export { RadioBox, RadioBoxGroup } from '@leafygreen-ui/radio-box-group'; export { Select, Option, Size as SelectSize } from '@leafygreen-ui/select'; export { Tabs, Tab } from '@leafygreen-ui/tabs'; export { default as TextArea } from '@leafygreen-ui/text-area'; diff --git a/packages/connect-form/src/advanced-options-tabs/general-tab.tsx b/packages/connect-form/src/advanced-options-tabs/general-tab.tsx deleted file mode 100644 index c9d652b67d3..00000000000 --- a/packages/connect-form/src/advanced-options-tabs/general-tab.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -function GeneralTab(): React.ReactElement { - return

General

; -} - -export default GeneralTab; diff --git a/packages/connect-form/src/advanced-connection-options.tsx b/packages/connect-form/src/components/advanced-connection-options.tsx similarity index 100% rename from packages/connect-form/src/advanced-connection-options.tsx rename to packages/connect-form/src/components/advanced-connection-options.tsx diff --git a/packages/connect-form/src/advanced-options-tabs/advanced-options-tabs.tsx b/packages/connect-form/src/components/advanced-options-tabs/advanced-options-tabs.tsx similarity index 75% rename from packages/connect-form/src/advanced-options-tabs/advanced-options-tabs.tsx rename to packages/connect-form/src/components/advanced-options-tabs/advanced-options-tabs.tsx index bab53a4df62..0a688d33cf0 100644 --- a/packages/connect-form/src/advanced-options-tabs/advanced-options-tabs.tsx +++ b/packages/connect-form/src/components/advanced-options-tabs/advanced-options-tabs.tsx @@ -11,14 +11,6 @@ interface TabObject { component: React.FunctionComponent; } -function renderTab(tabObject: TabObject, idx: number): React.ReactElement { - const TabComponent = tabObject.component; - return ( - - - - ); -} function AdvancedOptionsTabs(): React.ReactElement { const [activeTab, setActiveTab] = useState(0); @@ -34,7 +26,15 @@ function AdvancedOptionsTabs(): React.ReactElement { selected={activeTab} aria-label="Advanced Options Tabs" > - {tabs.map(renderTab)} + {tabs.map((tabObject: TabObject, idx: number) => { + const TabComponent = tabObject.component; + + return ( + + + + ); + })} ); } diff --git a/packages/connect-form/src/advanced-options-tabs/advanced-tab.tsx b/packages/connect-form/src/components/advanced-options-tabs/advanced-tab.tsx similarity index 100% rename from packages/connect-form/src/advanced-options-tabs/advanced-tab.tsx rename to packages/connect-form/src/components/advanced-options-tabs/advanced-tab.tsx diff --git a/packages/connect-form/src/components/advanced-options-tabs/general-tab.tsx b/packages/connect-form/src/components/advanced-options-tabs/general-tab.tsx new file mode 100644 index 00000000000..05a28cf86dc --- /dev/null +++ b/packages/connect-form/src/components/advanced-options-tabs/general-tab.tsx @@ -0,0 +1,119 @@ +import { css } from '@emotion/css'; +import React from 'react'; +import { + Checkbox, + Label, + Icon, + IconButton, + TextInput, + spacing, +} from '@mongodb-js/compass-components'; + +import { useConnectionStringContext } from '../../contexts/connection-string-context'; +import SchemaInput from './general/schema-input'; + +const hostInputContainer = css({ + display: 'flex', + flexDirection: 'row', + width: '100%', + alignItems: 'center', + marginBottom: spacing[2], +}); + +const hostInput = css({ + flexGrow: 1, +}); + +function GeneralTab(): React.ReactElement { + const [connectionStringUrl, { setConnectionString }] = + useConnectionStringContext(); + + return ( +
+ + + {connectionStringUrl.hosts.map((host, index) => ( +
+ { + const updatedConnectionString = connectionStringUrl.clone(); + // TODO: Validation on the hostname. + // Keep in state and allow invalid. + + updatedConnectionString.hosts[index] = event.target.value; + + setConnectionString(updatedConnectionString.toString()); + }} + /> + + {/* TODO: Should we still show a + but then give them a message when they try and click w/ srvs? */} + {!connectionStringUrl.isSRV && ( + { + const updatedConnectionString = connectionStringUrl.clone(); + + // TODO: Default new host name? + updatedConnectionString.hosts.push(''); + + setConnectionString(updatedConnectionString.toString()); + }} + > + + + )} + {!connectionStringUrl.isSRV && connectionStringUrl.hosts.length > 1 && ( + { + const updatedConnectionString = connectionStringUrl.clone(); + + updatedConnectionString.hosts.splice(index, 1); + + setConnectionString(updatedConnectionString.toString()); + }} + > + + + )} +
+ ))} + + { + // TODO: Ensure it's a valid connection string first? try catch? + const updatedConnectionString = connectionStringUrl.clone(); + if (event.target.checked) { + updatedConnectionString.searchParams.set( + 'directConnection', + 'true' + ); + } else if ( + updatedConnectionString.searchParams.get('directConnection') + ) { + updatedConnectionString.searchParams.delete('directConnection'); + } + + setConnectionString(updatedConnectionString.toString()); + }} + label="Direct Connection" + checked={ + connectionStringUrl.searchParams.get('directConnection') === 'true' + } + bold={false} + /> +
+ ); +} + +export default GeneralTab; diff --git a/packages/connect-form/src/components/advanced-options-tabs/general/schema-input.tsx b/packages/connect-form/src/components/advanced-options-tabs/general/schema-input.tsx new file mode 100644 index 00000000000..00e9869f66a --- /dev/null +++ b/packages/connect-form/src/components/advanced-options-tabs/general/schema-input.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { + Description, + Label, + RadioBox, + RadioBoxGroup, +} from '@mongodb-js/compass-components'; + +import { useConnectionStringContext } from '../../../contexts/connection-string-context'; + +enum MONGODB_SCHEMA { + MONGODB = 'MONGODB', + MONGODB_SRV = 'MONGODB_SRV', +} + +const regularSchemaDescription = + 'Standard Connection String Format. The standard format of the MongoDB connection URI is used to connect to a MongoDB deployment: standalone, replica set, or a sharded cluster.'; +const srvSchemaDescription = + 'DNS Seed List Connection Format. The +srv indicates to the client that the hostname that follows corresponds to a DNS SRV record.'; + +function SchemaInput(): React.ReactElement { + const [{ connectionStringUrl }, { setConnectionString }] = + useConnectionStringContext(); + + const { isSRV } = connectionStringUrl; + + // const connection + + return ( + <> + + ) => { + if (event.target.value === MONGODB_SCHEMA.MONGODB) { + if (!isSRV) { + // Skip if already srv (re-clicked option). + return; + } + // Chose regular schema. + // TODO: Add port if not exists (coming from srv) + // First check if srv then avoid if not. + + setConnectionString('mongodb://localhost:27017'); + } else { + // Chose srv. + // TODO: + // Remove additional hosts if they exist. + // Remove port. + setConnectionString('mongodb+srv://localhost'); + } + // TODO: Ensure it's a valid connection string first? try catch? + }} + > + mongodb + mongodb+srv + + + {isSRV ? srvSchemaDescription : regularSchemaDescription} + + + ); +} + +export default SchemaInput; diff --git a/packages/connect-form/src/advanced-options-tabs/ssh-tunnel-tab.tsx b/packages/connect-form/src/components/advanced-options-tabs/ssh-tunnel-tab.tsx similarity index 100% rename from packages/connect-form/src/advanced-options-tabs/ssh-tunnel-tab.tsx rename to packages/connect-form/src/components/advanced-options-tabs/ssh-tunnel-tab.tsx diff --git a/packages/connect-form/src/advanced-options-tabs/ssl-tab.tsx b/packages/connect-form/src/components/advanced-options-tabs/ssl-tab.tsx similarity index 100% rename from packages/connect-form/src/advanced-options-tabs/ssl-tab.tsx rename to packages/connect-form/src/components/advanced-options-tabs/ssl-tab.tsx diff --git a/packages/connect-form/src/confirm-edit-connection-string.spec.tsx b/packages/connect-form/src/components/confirm-edit-connection-string.spec.tsx similarity index 100% rename from packages/connect-form/src/confirm-edit-connection-string.spec.tsx rename to packages/connect-form/src/components/confirm-edit-connection-string.spec.tsx diff --git a/packages/connect-form/src/confirm-edit-connection-string.tsx b/packages/connect-form/src/components/confirm-edit-connection-string.tsx similarity index 100% rename from packages/connect-form/src/confirm-edit-connection-string.tsx rename to packages/connect-form/src/components/confirm-edit-connection-string.tsx diff --git a/packages/connect-form/src/components/connect-form-actions.tsx b/packages/connect-form/src/components/connect-form-actions.tsx new file mode 100644 index 00000000000..3fbf897390f --- /dev/null +++ b/packages/connect-form/src/components/connect-form-actions.tsx @@ -0,0 +1,37 @@ +import { css } from '@emotion/css'; +import React from 'react'; +import { + Button, + ButtonVariant, + spacing, + uiColors, +} from '@mongodb-js/compass-components'; + +import { useConnectionStringContext } from '../contexts/connection-string-context'; + +const formActionStyles = css({ + padding: spacing[4], + borderTop: `1px solid ${uiColors.gray.light2}`, + textAlign: 'right', +}); + +function ConnectFormActions({ + onConnectClicked, +}: { + onConnectClicked: (connectionString: string) => void; +}): React.ReactElement { + const [connectionStringUrl] = useConnectionStringContext(); + + return ( +
+ +
+ ); +} + +export default ConnectFormActions; diff --git a/packages/connect-form/src/connect-form.spec.tsx b/packages/connect-form/src/components/connect-form.spec.tsx similarity index 86% rename from packages/connect-form/src/connect-form.spec.tsx rename to packages/connect-form/src/components/connect-form.spec.tsx index a68fbe3e6fc..179fca7af8d 100644 --- a/packages/connect-form/src/connect-form.spec.tsx +++ b/packages/connect-form/src/components/connect-form.spec.tsx @@ -10,10 +10,8 @@ function renderForm() { onConnectClicked={() => { /* */ }} - initialConnectionInfo={{ - connectionOptions: { - connectionString: 'mongodb://pineapple:orangutans@localhost:27019', - }, + initialConnectionOptions={{ + connectionString: 'mongodb://pineapple:orangutans@localhost:27019', }} /> ); diff --git a/packages/connect-form/src/components/connect-form.tsx b/packages/connect-form/src/components/connect-form.tsx new file mode 100644 index 00000000000..a2882e61a4f --- /dev/null +++ b/packages/connect-form/src/components/connect-form.tsx @@ -0,0 +1,98 @@ +import { css } from '@emotion/css'; +import React, { useMemo, useState } from 'react'; +import { ConnectionOptions } from 'mongodb-data-service'; +import { Card, Description, H3, spacing } from '@mongodb-js/compass-components'; +import ConnectionStringUrl from 'mongodb-connection-string-url'; + +import ConnectionStringInput from './connection-string-input'; +import AdvancedConnectionOptions from './advanced-connection-options'; +import ConnectionStringContext from '../contexts/connection-string-context'; +import ConnectFormActions from './connect-form-actions'; + +const formContainerStyles = css({ + margin: 0, + padding: spacing[4], + height: 'fit-content', + flexGrow: 1, + minWidth: 400, + maxWidth: 760, + position: 'relative', + display: 'inline-block', +}); + +const formCardStyles = css({ + margin: 0, + padding: spacing[2], + height: 'fit-content', + width: '100%', + position: 'relative', +}); + +const descriptionStyles = css({ + marginTop: spacing[2], +}); + +const formContentContainerStyles = css({ + padding: spacing[4], +}); + +function ConnectForm({ + initialConnectionOptions, + onConnectClicked, +}: { + initialConnectionOptions: ConnectionOptions; + onConnectClicked: (connectionOptions: ConnectionOptions) => void; +}): React.ReactElement { + // TODO: The initial connection string can be invalid. + // + + // const connectionStringUrl = + // useMemo((): ConnectionStringUrl => { + // try { + // return new ConnectionStringUrl( + // initialConnectionOptions.connectionString + // ); + // // + // } catch (error) { + // // TODO: Pass default connection string when can't be parsed. + + // // TODO: This should disable the form? + // return new ConnectionStringUrl(''); + // } + // }, [initialConnectionOptions]); + + // initialConnectionOptions.connectionString + // const connectionStringUrl = useRef(initialConnectionStringUrl); + + // TODO: Which as value? Plain string or typed? + // Plain for the connection string input to show the error. + + return ( + +
+ +
+

New Connection

+ + Connect to a MongoDB deployment + + + +
+ + onConnectClicked({ + ...initialConnectionOptions, + connectionString, + }) + } + /> +
+
+
+ ); +} + +export default ConnectForm; diff --git a/packages/connect-form/src/connection-string-input.spec.tsx b/packages/connect-form/src/components/connection-string-input.spec.tsx similarity index 100% rename from packages/connect-form/src/connection-string-input.spec.tsx rename to packages/connect-form/src/components/connection-string-input.spec.tsx diff --git a/packages/connect-form/src/connection-string-input.tsx b/packages/connect-form/src/components/connection-string-input.tsx similarity index 93% rename from packages/connect-form/src/connection-string-input.tsx rename to packages/connect-form/src/components/connection-string-input.tsx index 81347c7492b..03ecdad4ce1 100644 --- a/packages/connect-form/src/connection-string-input.tsx +++ b/packages/connect-form/src/components/connection-string-input.tsx @@ -11,6 +11,8 @@ import { import ConfirmEditConnectionString from './confirm-edit-connection-string'; import ConnectionStringUrl from 'mongodb-connection-string-url'; +import { useConnectionStringContext } from '../contexts/connection-string-context'; + const uriLabelStyles = css({ padding: 0, margin: 0, @@ -120,18 +122,16 @@ export function hidePasswordInConnectionString( } } -function ConnectStringInput({ - connectionString, - setConnectionString, -}: { - connectionString: string; - setConnectionString: (connectionString: string) => void; -}): React.ReactElement { +function ConnectStringInput(): React.ReactElement { + const [connectionString, { setConnectionString }] = + useConnectionStringContext(); + const [ { enableEditingConnectionString, showConfirmEditConnectionStringPrompt }, dispatch, ] = useReducer(reducer, { // If there is a connection string default it to protected. + // TODO: Should we just default it to not protected if there is nothing to hide? enableEditingConnectionString: !connectionString, showConfirmEditConnectionStringPrompt: false, }); @@ -139,8 +139,8 @@ function ConnectStringInput({ const textAreaEl = useRef(null); const displayedConnectionString = enableEditingConnectionString - ? connectionString - : hidePasswordInConnectionString(connectionString); + ? connectionString.toString() + : hidePasswordInConnectionString(connectionString.toString()); return ( diff --git a/packages/connect-form/src/connect-form.tsx b/packages/connect-form/src/connect-form.tsx deleted file mode 100644 index 9b439c62d1d..00000000000 --- a/packages/connect-form/src/connect-form.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { css } from '@emotion/css'; -import React, { useState } from 'react'; -import { ConnectionInfo } from 'mongodb-data-service'; -import { - Button, - ButtonVariant, - Card, - Description, - H3, - spacing, - uiColors, -} from '@mongodb-js/compass-components'; - -import ConnectionStringInput from './connection-string-input'; -import AdvancedConnectionOptions from './advanced-connection-options'; - -const formContainerStyles = css({ - margin: 0, - padding: spacing[4], - height: 'fit-content', - flexGrow: 1, - minWidth: 400, - maxWidth: 760, - position: 'relative', - display: 'inline-block', -}); - -const formCardStyles = css({ - margin: 0, - padding: spacing[2], - height: 'fit-content', - width: '100%', - position: 'relative', -}); - -const descriptionStyles = css({ - marginTop: spacing[2], -}); - -const formContentContainerStyles = css({ - padding: spacing[4], -}); - -const formActionStyles = css({ - padding: spacing[4], - borderTop: `1px solid ${uiColors.gray.light2}`, - textAlign: 'right', -}); - -function ConnectForm({ - initialConnectionInfo, - onConnectClicked, -}: { - initialConnectionInfo: ConnectionInfo; - onConnectClicked: (connectionInfo: ConnectionInfo) => void; -}): React.ReactElement { - const [connectionString, setConnectionString] = useState( - initialConnectionInfo.connectionOptions.connectionString - ); - - return ( -
- -
-

New Connection

- - Connect to a MongoDB deployment - - - -
-
- -
-
-
- ); -} - -export default ConnectForm; diff --git a/packages/connect-form/src/contexts/connection-string-context.ts b/packages/connect-form/src/contexts/connection-string-context.ts new file mode 100644 index 00000000000..a82f645d0c4 --- /dev/null +++ b/packages/connect-form/src/contexts/connection-string-context.ts @@ -0,0 +1,57 @@ +import { createContext, useContext } from 'react'; +import ConnectionStringUrl from 'mongodb-connection-string-url'; + +const ConnectionStringContext = createContext(''); +export default ConnectionStringContext; + +interface State { + connectionString: string; + connectionStringInvalidError: string; + connectionStringUrl: ConnectionStringUrl; +} + +export const useConnectionStringContext = (): [ + State, + { + setConnectionString: (connectionString: string) => void; + } +] => { + const context = useContext(ConnectionStringContext); + + if (!context) { + throw new Error( + 'useConnectionStringContext must be used within a ConnectionStringContext Provider' + ); + } + + const connectionStringInvalidError = ''; + + const connectionStringUrl = new ConnectionStringUrl( + 'mongodb://localhost:27017' + ); + + // TODO: Signature is ? + function setConnectionString(connectionString: string) { + try { + // connectionString; + } catch (e) { + // Should we trust anything they send is valid? + // if (connectionStringInvalidError) { + // } + } + } + + // TODO: Should we a different kind of refresh + // Should we pull the individual props used here? + + return [ + { + connectionString, + connectionStringUrl, + connectionStringInvalidError, + }, + { + setConnectionString, + }, + ]; +}; diff --git a/packages/connect-form/src/index.ts b/packages/connect-form/src/index.ts index b74dc54622c..11da4806d55 100644 --- a/packages/connect-form/src/index.ts +++ b/packages/connect-form/src/index.ts @@ -1,3 +1,3 @@ -import ConnectForm from './connect-form'; +import ConnectForm from './components/connect-form'; export default ConnectForm; diff --git a/packages/connections/src/components/connections.tsx b/packages/connections/src/components/connections.tsx index 22c104fe104..b0473048e49 100644 --- a/packages/connections/src/components/connections.tsx +++ b/packages/connections/src/components/connections.tsx @@ -98,7 +98,7 @@ function Connections({
connect(connectionInfo)} - initialConnectionInfo={activeConnectionInfo} + initialConnectionOptions={activeConnectionInfo.connectionOptions} key={activeConnectionId} /> From 3aaf56bdb660a92741e308a47cef5d99984a80c0 Mon Sep 17 00:00:00 2001 From: Anemy Date: Mon, 22 Nov 2021 16:49:33 -0500 Subject: [PATCH 02/29] update connection general tab state, keep state between swaps --- .../advanced-connection-options.tsx | 14 +- .../advanced-options-tabs.tsx | 19 +- .../advanced-options-tabs/general-tab.tsx | 84 ++++--- .../general/schema-input.tsx | 70 +++++- .../src/components/connect-form-actions.tsx | 8 +- .../src/components/connect-form.tsx | 210 ++++++++++++++---- .../components/connection-string-input.tsx | 71 +++++- .../src/contexts/connection-string-context.ts | 2 +- .../src/components/connections.tsx | 9 +- .../src/stores/connections-store.ts | 2 + 10 files changed, 385 insertions(+), 104 deletions(-) diff --git a/packages/connect-form/src/components/advanced-connection-options.tsx b/packages/connect-form/src/components/advanced-connection-options.tsx index 7f674aa4697..4c76031cd13 100644 --- a/packages/connect-form/src/components/advanced-connection-options.tsx +++ b/packages/connect-form/src/components/advanced-connection-options.tsx @@ -1,11 +1,21 @@ import React from 'react'; import { Accordion } from '@mongodb-js/compass-components'; import AdvancedOptionsTabs from './advanced-options-tabs/advanced-options-tabs'; +import ConnectionStringUrl from 'mongodb-connection-string-url'; -function AdvancedConnectionOptions(): React.ReactElement { +function AdvancedConnectionOptions({ + connectionStringUrl, + setConnectionStringUrl, +}: { + connectionStringUrl: ConnectionStringUrl; + setConnectionStringUrl: (connectionStringUrl: ConnectionStringUrl) => void; +}): React.ReactElement { return ( - + ); } diff --git a/packages/connect-form/src/components/advanced-options-tabs/advanced-options-tabs.tsx b/packages/connect-form/src/components/advanced-options-tabs/advanced-options-tabs.tsx index 0a688d33cf0..a0f44011e32 100644 --- a/packages/connect-form/src/components/advanced-options-tabs/advanced-options-tabs.tsx +++ b/packages/connect-form/src/components/advanced-options-tabs/advanced-options-tabs.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import { Tabs, Tab } from '@mongodb-js/compass-components'; +import ConnectionStringUrl from 'mongodb-connection-string-url'; import GeneralTab from './general-tab'; import SSLTab from './ssl-tab'; @@ -8,10 +9,19 @@ import AdvancedTab from './advanced-tab'; interface TabObject { name: string; - component: React.FunctionComponent; + component: React.FunctionComponent<{ + connectionStringUrl: ConnectionStringUrl; + setConnectionStringUrl: (connectionStringUrl: ConnectionStringUrl) => void; + }>; } -function AdvancedOptionsTabs(): React.ReactElement { +function AdvancedOptionsTabs({ + connectionStringUrl, + setConnectionStringUrl, +}: { + connectionStringUrl: ConnectionStringUrl; + setConnectionStringUrl: (connectionStringUrl: ConnectionStringUrl) => void; +}): React.ReactElement { const [activeTab, setActiveTab] = useState(0); const tabs: TabObject[] = [ @@ -31,7 +41,10 @@ function AdvancedOptionsTabs(): React.ReactElement { return ( - + ); })} diff --git a/packages/connect-form/src/components/advanced-options-tabs/general-tab.tsx b/packages/connect-form/src/components/advanced-options-tabs/general-tab.tsx index 05a28cf86dc..35a822ecaf0 100644 --- a/packages/connect-form/src/components/advanced-options-tabs/general-tab.tsx +++ b/packages/connect-form/src/components/advanced-options-tabs/general-tab.tsx @@ -8,8 +8,9 @@ import { TextInput, spacing, } from '@mongodb-js/compass-components'; +import ConnectionStringUrl from 'mongodb-connection-string-url'; -import { useConnectionStringContext } from '../../contexts/connection-string-context'; +// import { useConnectionStringContext } from '../../contexts/connection-string-context'; import SchemaInput from './general/schema-input'; const hostInputContainer = css({ @@ -24,13 +25,27 @@ const hostInput = css({ flexGrow: 1, }); -function GeneralTab(): React.ReactElement { - const [connectionStringUrl, { setConnectionString }] = - useConnectionStringContext(); +function GeneralTab({ + connectionStringUrl, + setConnectionStringUrl, +}: { + connectionStringUrl: ConnectionStringUrl; + setConnectionStringUrl: (connectionStringUrl: ConnectionStringUrl) => void; +}): React.ReactElement { + // const [connectionStringUrl, { setConnectionString }] = + // useConnectionStringContext(); + + if (!connectionStringUrl) { + // TODO: Make this required to have a value? + return
No connectionStringUrl
; + } return (
- + @@ -46,13 +61,20 @@ function GeneralTab(): React.ReactElement { // aria-label="Connection Hostname(s)" value={host} onChange={(event) => { + // const updatedHosts = [ + // ...updatedConnectionString.hosts + // ]; + // updatedHosts[index] = event.target.value; + // setConnectionItem('hosts', ) + const updatedConnectionString = connectionStringUrl.clone(); // TODO: Validation on the hostname. // Keep in state and allow invalid. updatedConnectionString.hosts[index] = event.target.value; - setConnectionString(updatedConnectionString.toString()); + // // TODO: Use different api setConnectionItem + setConnectionStringUrl(updatedConnectionString); }} /> @@ -66,7 +88,7 @@ function GeneralTab(): React.ReactElement { // TODO: Default new host name? updatedConnectionString.hosts.push(''); - setConnectionString(updatedConnectionString.toString()); + setConnectionStringUrl(updatedConnectionString); }} > @@ -80,7 +102,7 @@ function GeneralTab(): React.ReactElement { updatedConnectionString.hosts.splice(index, 1); - setConnectionString(updatedConnectionString.toString()); + setConnectionStringUrl(updatedConnectionString); }} > @@ -89,29 +111,31 @@ function GeneralTab(): React.ReactElement {
))} - { - // TODO: Ensure it's a valid connection string first? try catch? - const updatedConnectionString = connectionStringUrl.clone(); - if (event.target.checked) { - updatedConnectionString.searchParams.set( - 'directConnection', - 'true' - ); - } else if ( - updatedConnectionString.searchParams.get('directConnection') - ) { - updatedConnectionString.searchParams.delete('directConnection'); - } + {!connectionStringUrl.isSRV && ( + { + // TODO: Ensure it's a valid connection string first? try catch? + const updatedConnectionString = connectionStringUrl.clone(); + if (event.target.checked) { + updatedConnectionString.searchParams.set( + 'directConnection', + 'true' + ); + } else if ( + updatedConnectionString.searchParams.get('directConnection') + ) { + updatedConnectionString.searchParams.delete('directConnection'); + } - setConnectionString(updatedConnectionString.toString()); - }} - label="Direct Connection" - checked={ - connectionStringUrl.searchParams.get('directConnection') === 'true' - } - bold={false} - /> + setConnectionStringUrl(updatedConnectionString); + }} + label="Direct Connection" + checked={ + connectionStringUrl.searchParams.get('directConnection') === 'true' + } + bold={false} + /> + )}
); } diff --git a/packages/connect-form/src/components/advanced-options-tabs/general/schema-input.tsx b/packages/connect-form/src/components/advanced-options-tabs/general/schema-input.tsx index 00e9869f66a..c624522c982 100644 --- a/packages/connect-form/src/components/advanced-options-tabs/general/schema-input.tsx +++ b/packages/connect-form/src/components/advanced-options-tabs/general/schema-input.tsx @@ -5,8 +5,9 @@ import { RadioBox, RadioBoxGroup, } from '@mongodb-js/compass-components'; +import ConnectionStringUrl from 'mongodb-connection-string-url'; -import { useConnectionStringContext } from '../../../contexts/connection-string-context'; +// import { useConnectionStringContext } from '../../../contexts/connection-string-context'; enum MONGODB_SCHEMA { MONGODB = 'MONGODB', @@ -18,9 +19,15 @@ const regularSchemaDescription = const srvSchemaDescription = 'DNS Seed List Connection Format. The +srv indicates to the client that the hostname that follows corresponds to a DNS SRV record.'; -function SchemaInput(): React.ReactElement { - const [{ connectionStringUrl }, { setConnectionString }] = - useConnectionStringContext(); +function SchemaInput({ + connectionStringUrl, + setConnectionStringUrl, +}: { + connectionStringUrl: ConnectionStringUrl; + setConnectionStringUrl: (connectionStringUrl: ConnectionStringUrl) => void; +}): React.ReactElement { + // const [{ connectionStringUrl }, { setConnectionString }] = + // useConnectionStringContext(); const { isSRV } = connectionStringUrl; @@ -33,22 +40,69 @@ function SchemaInput(): React.ReactElement { id="connection-schema-radio-box-group" value={isSRV ? MONGODB_SCHEMA.MONGODB_SRV : MONGODB_SCHEMA.MONGODB} onChange={(event: React.ChangeEvent) => { + // TODO: Assign default or always have connection string. + // TODO: Maybe have a more distinct place for this conversion? if (event.target.value === MONGODB_SCHEMA.MONGODB) { if (!isSRV) { - // Skip if already srv (re-clicked option). + // Skip if already not srv (re-clicked option). return; } + + // newConnectionString + + const newConnectionString = connectionStringUrl.toString(); + + const newConnectionStringUrl = new ConnectionStringUrl( + newConnectionString.replace('mongodb+srv://', 'mongodb://') + ); + + newConnectionStringUrl.hosts = [ + `${newConnectionStringUrl.hosts[0]}:27017`, + ]; + // Chose regular schema. // TODO: Add port if not exists (coming from srv) - // First check if srv then avoid if not. - setConnectionString('mongodb://localhost:27017'); + // new ConnectionStringUrl( + // newConnectionString.replace('mongodb+srv://', 'mongodb://') + // ) + + setConnectionStringUrl(newConnectionStringUrl); } else { + let newConnectionStringUrl = connectionStringUrl.clone(); + // // Only include one host without port. + newConnectionStringUrl.hosts = [ + newConnectionStringUrl.hosts[0].substring( + 0, + newConnectionStringUrl.hosts[0].indexOf(':') === -1 + ? undefined + : newConnectionStringUrl.hosts[0].indexOf(':') + ), + ]; + const newConnectionString = newConnectionStringUrl.toString(); + + newConnectionStringUrl = new ConnectionStringUrl( + newConnectionString.replace('mongodb://', 'mongodb+srv://') + ); + + if (newConnectionStringUrl.searchParams.get('directConnection')) { + newConnectionStringUrl.searchParams.delete('directConnection'); + } + // Chose srv. // TODO: // Remove additional hosts if they exist. // Remove port. - setConnectionString('mongodb+srv://localhost'); + // Remove direct connection + // setConnectionStringUrl( + // new ConnectionStringUrl( + // newConnectionString.replace('mongodb://', 'mongodb+srv://') + // ) + // ); + // setConnectionStringUrl( + // new ConnectionStringUrl('mongodb+srv://one,two,three') + // ); + setConnectionStringUrl(newConnectionStringUrl); } // TODO: Ensure it's a valid connection string first? try catch? }} diff --git a/packages/connect-form/src/components/connect-form-actions.tsx b/packages/connect-form/src/components/connect-form-actions.tsx index 3fbf897390f..85aa6830d03 100644 --- a/packages/connect-form/src/components/connect-form-actions.tsx +++ b/packages/connect-form/src/components/connect-form-actions.tsx @@ -7,8 +7,6 @@ import { uiColors, } from '@mongodb-js/compass-components'; -import { useConnectionStringContext } from '../contexts/connection-string-context'; - const formActionStyles = css({ padding: spacing[4], borderTop: `1px solid ${uiColors.gray.light2}`, @@ -18,15 +16,13 @@ const formActionStyles = css({ function ConnectFormActions({ onConnectClicked, }: { - onConnectClicked: (connectionString: string) => void; + onConnectClicked: () => void; }): React.ReactElement { - const [connectionStringUrl] = useConnectionStringContext(); - return (
diff --git a/packages/connect-form/src/components/connect-form.tsx b/packages/connect-form/src/components/connect-form.tsx index a2882e61a4f..abfd24988b1 100644 --- a/packages/connect-form/src/components/connect-form.tsx +++ b/packages/connect-form/src/components/connect-form.tsx @@ -1,12 +1,11 @@ import { css } from '@emotion/css'; -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useReducer } from 'react'; import { ConnectionOptions } from 'mongodb-data-service'; import { Card, Description, H3, spacing } from '@mongodb-js/compass-components'; import ConnectionStringUrl from 'mongodb-connection-string-url'; import ConnectionStringInput from './connection-string-input'; import AdvancedConnectionOptions from './advanced-connection-options'; -import ConnectionStringContext from '../contexts/connection-string-context'; import ConnectFormActions from './connect-form-actions'; const formContainerStyles = css({ @@ -36,6 +35,130 @@ const formContentContainerStyles = css({ padding: spacing[4], }); +interface State { + connectionStringInvalidError: string | null; + connectionStringUrl: ConnectionStringUrl; +} + +type Action = + | { + type: 'set-connection-string-error'; + errorMessage: string | null; + } + | { + type: 'set-connection-string-url'; + connectionStringUrl: ConnectionStringUrl; + } + | { + type: 'set-connection-string-state'; + connectionStringInvalidError: string | null; + connectionStringUrl: ConnectionStringUrl; + }; + +function connectFormReducer(state: State, action: Action): State { + switch (action.type) { + case 'set-connection-string-error': + return { + ...state, + connectionStringInvalidError: action.errorMessage, + }; + case 'set-connection-string-url': + return { + ...state, + connectionStringUrl: action.connectionStringUrl, + }; + case 'set-connection-string-state': + return { + ...state, + connectionStringUrl: action.connectionStringUrl, + connectionStringInvalidError: action.connectionStringInvalidError, + }; + } +} + +function parseConnectionUrlFromOptions( + initialConnectionOptions: ConnectionOptions +) { + let connectionStringInvalidError = null; + // TODO: Have a default connection string variable somewhere. + let connectionStringUrl = new ConnectionStringUrl( + 'mongodb://localhost:27017' + ); + try { + connectionStringUrl = new ConnectionStringUrl( + initialConnectionOptions.connectionString + ); + } catch (error) { + connectionStringInvalidError = (error as Error).message; + } + return { + connectionStringInvalidError, + connectionStringUrl, + }; +} + +function useConnectForm(initialConnectionOptions: ConnectionOptions): [ + State, + { + setConnectionStringError: (errorMessage: string | null) => void; + setConnectionStringUrl: (connectionStringUrl: ConnectionStringUrl) => void; + setConnectionItem: (name: string, value: string) => void; + setConnectionStringQueryItem: (name: string, value: string) => void; + } +] { + // TODO: Try to validate connection string - if invalid disable options? + const [state, dispatch] = useReducer( + connectFormReducer, + parseConnectionUrlFromOptions(initialConnectionOptions) + ); + + useEffect(() => { + // When the initial connection options change, like a different + // connection is clicked in the compass-sidebar, we + // refresh the current connection string being edited. + // We do this here to retain the tabs/expanded accordion states. + const { connectionStringInvalidError, connectionStringUrl } = + parseConnectionUrlFromOptions(initialConnectionOptions); + + dispatch({ + type: 'set-connection-string-state', + connectionStringInvalidError, + connectionStringUrl, + }); + }, [initialConnectionOptions]); + + // const {} = useMemo // initialConnectionOptions + + return [ + state, + { + setConnectionStringError: (errorMessage: string | null) => { + dispatch({ + type: 'set-connection-string-error', + errorMessage, + }); + }, + setConnectionStringUrl: (connectionStringUrl: ConnectionStringUrl) => { + console.log('setConnectionStringUrl', connectionStringUrl); + console.log('setConnectionStringUrl', connectionStringUrl.toString()); + dispatch({ + type: 'set-connection-string-url', + connectionStringUrl, + }); + }, + setConnectionItem: (name: string, value: string) => { + // + // TODO: Try to set the item on the current connection string url. + // If it works cool. + // If not error. + }, + setConnectionStringQueryItem: (name: string, value: string) => { + // + }, + }, + ]; +} + function ConnectForm({ initialConnectionOptions, onConnectClicked, @@ -43,55 +166,56 @@ function ConnectForm({ initialConnectionOptions: ConnectionOptions; onConnectClicked: (connectionOptions: ConnectionOptions) => void; }): React.ReactElement { + const [ + { + // connectionStringInvalidError, + connectionStringUrl, + connectionStringInvalidError, + }, + { + setConnectionStringUrl, + setConnectionStringError, + // setConnectionItem, + // setConnectionStringQueryItem + }, + ] = useConnectForm(initialConnectionOptions); + // TODO: The initial connection string can be invalid. // - // const connectionStringUrl = - // useMemo((): ConnectionStringUrl => { - // try { - // return new ConnectionStringUrl( - // initialConnectionOptions.connectionString - // ); - // // - // } catch (error) { - // // TODO: Pass default connection string when can't be parsed. - - // // TODO: This should disable the form? - // return new ConnectionStringUrl(''); - // } - // }, [initialConnectionOptions]); - - // initialConnectionOptions.connectionString - // const connectionStringUrl = useRef(initialConnectionStringUrl); - // TODO: Which as value? Plain string or typed? // Plain for the connection string input to show the error. + const editingConnectionStringUrl = connectionStringUrl; + return ( - -
- -
-

New Connection

- - Connect to a MongoDB deployment - - - -
- - onConnectClicked({ - ...initialConnectionOptions, - connectionString, - }) - } +
+ +
+

New Connection

+ + Connect to a MongoDB deployment + + + - -
- +
+ + onConnectClicked({ + ...initialConnectionOptions, + connectionString: editingConnectionStringUrl.toString(), + }) + } + /> +
+
); } diff --git a/packages/connect-form/src/components/connection-string-input.tsx b/packages/connect-form/src/components/connection-string-input.tsx index 03ecdad4ce1..2e4f33c665c 100644 --- a/packages/connect-form/src/components/connection-string-input.tsx +++ b/packages/connect-form/src/components/connection-string-input.tsx @@ -1,5 +1,11 @@ import { css } from '@emotion/css'; -import React, { ChangeEvent, Fragment, useRef, useReducer } from 'react'; +import React, { + ChangeEvent, + Fragment, + useRef, + useReducer, + useEffect, +} from 'react'; import { Icon, IconButton, @@ -11,7 +17,7 @@ import { import ConfirmEditConnectionString from './confirm-edit-connection-string'; import ConnectionStringUrl from 'mongodb-connection-string-url'; -import { useConnectionStringContext } from '../contexts/connection-string-context'; +// import { useConnectionStringContext } from '../contexts/connection-string-context'; const uriLabelStyles = css({ padding: 0, @@ -57,18 +63,28 @@ const textAreaLabelContainerStyles = css({ const connectionStringInputId = 'connectionString'; type State = { + editingConnectionString: string; enableEditingConnectionString: boolean; showConfirmEditConnectionStringPrompt: boolean; }; type Action = | { type: 'enable-editing-connection-string' } + | { + type: 'set-editingConnectionString-connection-string'; + editingConnectionString: string; + } | { type: 'show-edit-connection-string-confirmation' } | { type: 'hide-edit-connection-string-confirmation' } | { type: 'hide-connection-string' }; function reducer(state: State, action: Action): State { switch (action.type) { + case 'set-editingConnectionString-connection-string': + return { + ...state, + editingConnectionString: action.editingConnectionString, + }; case 'enable-editing-connection-string': return { ...state, @@ -122,25 +138,49 @@ export function hidePasswordInConnectionString( } } -function ConnectStringInput(): React.ReactElement { - const [connectionString, { setConnectionString }] = - useConnectionStringContext(); +function ConnectStringInput({ + connectionString, + setConnectionStringError, + setConnectionStringUrl, +}: { + connectionString?: string; + setConnectionStringError: (errorMessage: string | null) => void; + setConnectionStringUrl: (connectionStringUrl: ConnectionStringUrl) => void; +}): React.ReactElement { + // const [connectionString, { setConnectionString }] = + // useConnectionStringContext(); + + // const [editingConnectionString, setConnectionString] = useState(connectionString || ''); const [ - { enableEditingConnectionString, showConfirmEditConnectionStringPrompt }, + { + editingConnectionString, + enableEditingConnectionString, + showConfirmEditConnectionStringPrompt, + }, dispatch, ] = useReducer(reducer, { // If there is a connection string default it to protected. // TODO: Should we just default it to not protected if there is nothing to hide? enableEditingConnectionString: !connectionString, showConfirmEditConnectionStringPrompt: false, + editingConnectionString: connectionString || '', }); + useEffect(() => { + if (!enableEditingConnectionString) { + dispatch({ + type: 'set-editingConnectionString-connection-string', + editingConnectionString: connectionString || '', + }); + } + }, [connectionString, enableEditingConnectionString]); + const textAreaEl = useRef(null); const displayedConnectionString = enableEditingConnectionString - ? connectionString.toString() - : hidePasswordInConnectionString(connectionString.toString()); + ? editingConnectionString + : hidePasswordInConnectionString(editingConnectionString); return ( @@ -186,7 +226,20 @@ function ConnectStringInput(): React.ReactElement {