From fb0d0fa9e53ed0da312a50815baa865b750e4eed Mon Sep 17 00:00:00 2001 From: Rhys Howell Date: Mon, 24 Feb 2025 12:42:46 -0500 Subject: [PATCH 1/4] chore(schema): add tooltip showing folks the export button when they legacy export schema --- .../src/components/compass-schema.tsx | 40 ++++-------- ...ner.tsx => export-schema-legacy-modal.tsx} | 29 +++++---- .../schema-toolbar.spec.tsx | 2 + .../{schema-toolbar => }/schema-toolbar.tsx | 53 +++++++++++---- .../src/stores/schema-export-reducer.ts | 65 +++++++++---------- packages/compass-schema/src/stores/store.ts | 4 +- 6 files changed, 105 insertions(+), 88 deletions(-) rename packages/compass-schema/src/components/{export-schema-legacy-banner.tsx => export-schema-legacy-modal.tsx} (96%) rename packages/compass-schema/src/components/{schema-toolbar => }/schema-toolbar.spec.tsx (97%) rename packages/compass-schema/src/components/{schema-toolbar => }/schema-toolbar.tsx (69%) diff --git a/packages/compass-schema/src/components/compass-schema.tsx b/packages/compass-schema/src/components/compass-schema.tsx index b2a742d5107..55359c028f8 100644 --- a/packages/compass-schema/src/components/compass-schema.tsx +++ b/packages/compass-schema/src/components/compass-schema.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import type { Schema as MongodbSchema } from 'mongodb-schema'; import { connect } from 'react-redux'; import type { AnalysisState } from '../constants/analysis-states'; @@ -7,11 +7,9 @@ import { ANALYSIS_STATE_ANALYZING, ANALYSIS_STATE_COMPLETE, } from '../constants/analysis-states'; - -import { SchemaToolbar } from './schema-toolbar/schema-toolbar'; +import SchemaToolbar from './schema-toolbar'; import Field from './field'; import { ZeroGraphic } from './zero-graphic'; - import { Button, CancelLoader, @@ -39,7 +37,7 @@ import type { RootState } from '../stores/store'; import { startAnalysis, stopAnalysis } from '../stores/schema-analysis-reducer'; import { openExportSchema } from '../stores/schema-export-reducer'; import ExportSchemaModal from './export-schema-modal'; -import ExportSchemaLegacyBanner from './export-schema-legacy-banner'; +import ExportSchemaLegacyModal from './export-schema-legacy-modal'; const rootStyles = css({ width: '100%', @@ -371,27 +369,16 @@ const PerformanceAdvisorBanner = () => { const Schema: React.FunctionComponent<{ analysisState: AnalysisState; - errorMessage?: string; - maxTimeMS?: number; schema: MongodbSchema | null; - count?: number; - resultId?: string; - onExportSchemaClicked: () => void; onStartAnalysis: () => Promise; onStopAnalysis: () => void; -}> = ({ - analysisState, - errorMessage, - schema, - resultId, - onExportSchemaClicked, - onStartAnalysis, - onStopAnalysis, -}) => { +}> = ({ analysisState, schema, onStartAnalysis, onStopAnalysis }) => { const onApplyClicked = useCallback(() => { void onStartAnalysis(); }, [onStartAnalysis]); + const [showLegacyExportTooltip, setShowLegacyExportTooltip] = useState(false); + const outdated = useIsLastAppliedQueryOutdated('schema'); const enablePerformanceAdvisorBanner = usePreference( @@ -407,13 +394,10 @@ const Schema: React.FunctionComponent<{ toolbar={ } > @@ -432,7 +416,11 @@ const Schema: React.FunctionComponent<{ {enableExportSchema && } - {enableExportSchema && } + {enableExportSchema && ( + + )} ); }; @@ -440,9 +428,7 @@ const Schema: React.FunctionComponent<{ export default connect( (state: RootState) => ({ analysisState: state.schemaAnalysis.analysisState, - errorMessage: state.schemaAnalysis.errorMessage, schema: state.schemaAnalysis.schema, - resultId: state.schemaAnalysis.resultId, }), { onStartAnalysis: startAnalysis, diff --git a/packages/compass-schema/src/components/export-schema-legacy-banner.tsx b/packages/compass-schema/src/components/export-schema-legacy-modal.tsx similarity index 96% rename from packages/compass-schema/src/components/export-schema-legacy-banner.tsx rename to packages/compass-schema/src/components/export-schema-legacy-modal.tsx index 71415ac892b..1720a37c531 100644 --- a/packages/compass-schema/src/components/export-schema-legacy-banner.tsx +++ b/packages/compass-schema/src/components/export-schema-legacy-modal.tsx @@ -16,7 +16,7 @@ import { confirmedExportLegacySchemaToClipboard, switchToSchemaExport, SchemaExportActions, - stopShowingLegacyBanner, + stopShowingLegacyModal, } from '../stores/schema-export-reducer'; const SchemaExportSVG = () => ( @@ -463,28 +463,31 @@ const optionHeaderStyles = css({ margin: '0px', }); -const ExportSchemaLegacyBanner: React.FunctionComponent<{ +const ExportSchemaLegacyModal: React.FunctionComponent<{ isOpen: boolean; onClose: () => void; onLegacyShare: () => void; onSwitchToSchemaExport: () => void; - stopShowingLegacyBanner: (choice: 'legacy' | 'export') => void; + setShowLegacyExportTooltip: (show: boolean) => void; + stopShowingLegacyModal: (choice: 'legacy' | 'export') => void; }> = ({ isOpen, onClose, onLegacyShare, onSwitchToSchemaExport, - stopShowingLegacyBanner, + setShowLegacyExportTooltip, + stopShowingLegacyModal, }) => { const [dontShowAgainChecked, setDontShowAgainChecked] = useState(false); const handleLegacyShare = useCallback(() => { - if (dontShowAgainChecked) stopShowingLegacyBanner('legacy'); + if (dontShowAgainChecked) stopShowingLegacyModal('legacy'); onLegacyShare(); - }, [onLegacyShare, dontShowAgainChecked, stopShowingLegacyBanner]); + setShowLegacyExportTooltip(true); + }, [onLegacyShare, dontShowAgainChecked, stopShowingLegacyModal]); const handleSwitchToNew = useCallback(() => { - if (dontShowAgainChecked) stopShowingLegacyBanner('export'); + if (dontShowAgainChecked) stopShowingLegacyModal('export'); onSwitchToSchemaExport(); - }, [onSwitchToSchemaExport, dontShowAgainChecked, stopShowingLegacyBanner]); + }, [onSwitchToSchemaExport, dontShowAgainChecked, stopShowingLegacyModal]); return ( ({ - isOpen: state.schemaExport.isLegacyBannerOpen, + isOpen: state.schemaExport.isLegacyModalOpen, }), (dispatch: SchemaThunkDispatch) => ({ - onClose: () => dispatch({ type: SchemaExportActions.closeLegacyBanner }), + onClose: () => dispatch({ type: SchemaExportActions.closeLegacyModal }), onLegacyShare: () => dispatch(confirmedExportLegacySchemaToClipboard()), onSwitchToSchemaExport: () => dispatch(switchToSchemaExport()), - stopShowingLegacyBanner: (choice: 'legacy' | 'export') => - dispatch(stopShowingLegacyBanner(choice)), + stopShowingLegacyModal: (choice: 'legacy' | 'export') => + dispatch(stopShowingLegacyModal(choice)), }) -)(ExportSchemaLegacyBanner); +)(ExportSchemaLegacyModal); diff --git a/packages/compass-schema/src/components/schema-toolbar/schema-toolbar.spec.tsx b/packages/compass-schema/src/components/schema-toolbar.spec.tsx similarity index 97% rename from packages/compass-schema/src/components/schema-toolbar/schema-toolbar.spec.tsx rename to packages/compass-schema/src/components/schema-toolbar.spec.tsx index 71c811aaff5..86cfe666de8 100644 --- a/packages/compass-schema/src/components/schema-toolbar/schema-toolbar.spec.tsx +++ b/packages/compass-schema/src/components/schema-toolbar.spec.tsx @@ -47,6 +47,8 @@ describe('SchemaToolbar', function () { sampleSize={10} schemaResultId="123" onExportSchemaClicked={() => {}} + setShowLegacyExportTooltip={() => {}} + showLegacyExportTooltip={false} {...props} /> , diff --git a/packages/compass-schema/src/components/schema-toolbar/schema-toolbar.tsx b/packages/compass-schema/src/components/schema-toolbar.tsx similarity index 69% rename from packages/compass-schema/src/components/schema-toolbar/schema-toolbar.tsx rename to packages/compass-schema/src/components/schema-toolbar.tsx index f84f6e13c87..289801f0d55 100644 --- a/packages/compass-schema/src/components/schema-toolbar/schema-toolbar.tsx +++ b/packages/compass-schema/src/components/schema-toolbar.tsx @@ -1,22 +1,26 @@ import React, { useMemo } from 'react'; +import { connect } from 'react-redux'; import { Body, Button, ErrorSummary, Icon, Link, + Tooltip, WarningSummary, css, spacing, } from '@mongodb-js/compass-components'; import { usePreference } from 'compass-preferences-model/provider'; -import type { AnalysisState } from '../../constants/analysis-states'; +import type { AnalysisState } from '../constants/analysis-states'; import { ANALYSIS_STATE_ERROR, ANALYSIS_STATE_TIMEOUT, ANALYSIS_STATE_COMPLETE, -} from '../../constants/analysis-states'; +} from '../constants/analysis-states'; import { QueryBar } from '@mongodb-js/compass-query-bar'; +import type { RootState } from '../stores/store'; +import { openExportSchema } from '../stores/schema-export-reducer'; const schemaToolbarStyles = css({ display: 'flex', @@ -67,9 +71,11 @@ type SchemaToolbarProps = { onResetClicked: () => void; sampleSize: number; schemaResultId: string; + setShowLegacyExportTooltip: (show: boolean) => void; + showLegacyExportTooltip: boolean; }; -const SchemaToolbar: React.FunctionComponent = ({ +export const SchemaToolbar: React.FunctionComponent = ({ analysisState, errorMessage, isOutdated, @@ -78,6 +84,8 @@ const SchemaToolbar: React.FunctionComponent = ({ onResetClicked, sampleSize, schemaResultId, + setShowLegacyExportTooltip, + showLegacyExportTooltip, }) => { const documentsNoun = useMemo( () => (sampleSize === 1 ? 'document' : 'documents'), @@ -101,15 +109,26 @@ const SchemaToolbar: React.FunctionComponent = ({
{enableExportSchema && ANALYSIS_STATE_COMPLETE && (
- + } > - Export Schema - + Next time, export the schema directly from Compass' Schema + tab. +
)}
= ({ ); }; -export { SchemaToolbar }; +export default connect( + (state: RootState) => ({ + analysisState: state.schemaAnalysis.analysisState, + errorMessage: state.schemaAnalysis.errorMessage, + sampleSize: state.schemaAnalysis.schema?.count ?? 0, + schemaResultId: state.schemaAnalysis.resultId ?? '', + }), + { + onExportSchemaClicked: openExportSchema, + } +)(SchemaToolbar); diff --git a/packages/compass-schema/src/stores/schema-export-reducer.ts b/packages/compass-schema/src/stores/schema-export-reducer.ts index 1c309eec250..49f9a8305a4 100644 --- a/packages/compass-schema/src/stores/schema-export-reducer.ts +++ b/packages/compass-schema/src/stores/schema-export-reducer.ts @@ -21,8 +21,8 @@ export type SchemaFormat = export type ExportStatus = 'inprogress' | 'complete' | 'error'; export type SchemaExportState = { isOpen: boolean; - isLegacyBannerOpen: boolean; - legacyBannerChoice?: 'legacy' | 'export'; + isLegacyModalOpen: boolean; + legacyModalChoice?: 'legacy' | 'export'; exportedSchema?: string; exportFormat: SchemaFormat; errorMessage?: string; @@ -37,16 +37,16 @@ const getInitialState = (): SchemaExportState => ({ exportStatus: 'inprogress', exportedSchema: undefined, isOpen: false, - isLegacyBannerOpen: false, - legacyBannerChoice: undefined, + isLegacyModalOpen: false, + legacyModalChoice: undefined, }); export const enum SchemaExportActions { openExportSchema = 'schema-service/schema-export/openExportSchema', closeExportSchema = 'schema-service/schema-export/closeExportSchema', - openLegacyBanner = 'schema-service/schema-export/openLegacyBanner', - closeLegacyBanner = 'schema-service/schema-export/closeLegacyBanner', - setLegacyBannerChoice = 'schema-service/schema-export/setLegacyBannerChoice', + openLegacyModal = 'schema-service/schema-export/openLegacyModal', + closeLegacyModal = 'schema-service/schema-export/closeLegacyModal', + setLegacyModalChoice = 'schema-service/schema-export/setLegacyModalChoice', changeExportSchemaStatus = 'schema-service/schema-export/changeExportSchemaStatus', changeExportSchemaFormatStarted = 'schema-service/schema-export/changeExportSchemaFormatStarted', changeExportSchemaFormatComplete = 'schema-service/schema-export/changeExportSchemaFormatComplete', @@ -316,38 +316,35 @@ export const schemaExportReducer: Reducer = ( } if ( - isAction( - action, - SchemaExportActions.openLegacyBanner - ) + isAction(action, SchemaExportActions.openLegacyModal) ) { return { ...state, - isLegacyBannerOpen: true, + isLegacyModalOpen: true, }; } if ( - isAction( + isAction( action, - SchemaExportActions.closeLegacyBanner + SchemaExportActions.closeLegacyModal ) ) { return { ...state, - isLegacyBannerOpen: false, + isLegacyModalOpen: false, }; } if ( - isAction( + isAction( action, - SchemaExportActions.setLegacyBannerChoice + SchemaExportActions.setLegacyModalChoice ) ) { return { ...state, - legacyBannerChoice: action.choice, + legacyModalChoice: action.choice, }; } @@ -407,19 +404,19 @@ export const schemaExportReducer: Reducer = ( return state; }; -// TODO clean out when phase out is confirmed COMPASS-8692 -export type openLegacyBannerAction = { - type: SchemaExportActions.openLegacyBanner; +// TODO(COMPASS-8692): clean out when phase out is confirmed. +export type openLegacyModalAction = { + type: SchemaExportActions.openLegacyModal; }; -export const openLegacyBanner = (): SchemaThunkAction => { +export const openLegacyModal = (): SchemaThunkAction => { return (dispatch, getState) => { - const choiceInState = getState().schemaExport.legacyBannerChoice; + const choiceInState = getState().schemaExport.legacyModalChoice; const savedChoice = choiceInState || localStorage.getItem(localStorageId); if (savedChoice) { if (savedChoice !== choiceInState) { dispatch({ - type: SchemaExportActions.setLegacyBannerChoice, + type: SchemaExportActions.setLegacyModalChoice, choice: savedChoice, }); } @@ -432,24 +429,24 @@ export const openLegacyBanner = (): SchemaThunkAction => { return; } } - dispatch({ type: SchemaExportActions.openLegacyBanner }); + dispatch({ type: SchemaExportActions.openLegacyModal }); }; }; -export type closeLegacyBannerAction = { - type: SchemaExportActions.closeLegacyBanner; +export type closeLegacyModalAction = { + type: SchemaExportActions.closeLegacyModal; }; -export type setLegacyBannerChoiceAction = { - type: SchemaExportActions.setLegacyBannerChoice; +export type setLegacyModalChoiceAction = { + type: SchemaExportActions.setLegacyModalChoice; choice: 'legacy' | 'export'; }; -const localStorageId = 'schemaExportLegacyBannerChoice'; +const localStorageId = 'schemaExportLegacyModalChoice'; export const switchToSchemaExport = (): SchemaThunkAction => { return (dispatch) => { - dispatch({ type: SchemaExportActions.closeLegacyBanner }); + dispatch({ type: SchemaExportActions.closeLegacyModal }); dispatch(openExportSchema()); }; }; @@ -471,7 +468,7 @@ export const confirmedExportLegacySchemaToClipboard = format: 'legacyJSON', }) ); - dispatch({ type: SchemaExportActions.closeLegacyBanner }); + dispatch({ type: SchemaExportActions.closeLegacyModal }); openToast( 'share-schema', hasSchema @@ -491,11 +488,11 @@ export const confirmedExportLegacySchemaToClipboard = }; }; -export const stopShowingLegacyBanner = ( +export const stopShowingLegacyModal = ( choice: 'legacy' | 'export' ): SchemaThunkAction => { return (dispatch) => { localStorage.setItem(localStorageId, choice); - dispatch({ type: SchemaExportActions.setLegacyBannerChoice, choice }); + dispatch({ type: SchemaExportActions.setLegacyModalChoice, choice }); }; }; diff --git a/packages/compass-schema/src/stores/store.ts b/packages/compass-schema/src/stores/store.ts index 076ddab4d2c..09d8054af0a 100644 --- a/packages/compass-schema/src/stores/store.ts +++ b/packages/compass-schema/src/stores/store.ts @@ -25,7 +25,7 @@ import { import { cancelExportSchema, confirmedExportLegacySchemaToClipboard, - openLegacyBanner, + openLegacyModal, schemaExportReducer, } from './schema-export-reducer'; import type { InternalLayer } from '../modules/geo'; @@ -85,7 +85,7 @@ export function activateSchemaPlugin( on(services.localAppRegistry, 'menu-share-schema-json', () => { const { enableExportSchema } = services.preferences.getPreferences(); if (enableExportSchema) { - store.dispatch(openLegacyBanner()); + store.dispatch(openLegacyModal()); return; } store.dispatch(confirmedExportLegacySchemaToClipboard()); From abfb001e64a50ce9452109936a3d50229ca88951 Mon Sep 17 00:00:00 2001 From: Rhys Howell Date: Tue, 25 Feb 2025 12:13:42 -0500 Subject: [PATCH 2/4] fixup: add react hook dep --- .../src/components/export-schema-legacy-modal.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/compass-schema/src/components/export-schema-legacy-modal.tsx b/packages/compass-schema/src/components/export-schema-legacy-modal.tsx index 1720a37c531..28d67b93803 100644 --- a/packages/compass-schema/src/components/export-schema-legacy-modal.tsx +++ b/packages/compass-schema/src/components/export-schema-legacy-modal.tsx @@ -483,7 +483,12 @@ const ExportSchemaLegacyModal: React.FunctionComponent<{ if (dontShowAgainChecked) stopShowingLegacyModal('legacy'); onLegacyShare(); setShowLegacyExportTooltip(true); - }, [onLegacyShare, dontShowAgainChecked, stopShowingLegacyModal]); + }, [ + onLegacyShare, + dontShowAgainChecked, + stopShowingLegacyModal, + setShowLegacyExportTooltip, + ]); const handleSwitchToNew = useCallback(() => { if (dontShowAgainChecked) stopShowingLegacyModal('export'); onSwitchToSchemaExport(); From 226d81c932c8715ee156f0dbc1e1592ce24cb0fc Mon Sep 17 00:00:00 2001 From: Rhys Howell Date: Wed, 26 Feb 2025 02:54:08 -0500 Subject: [PATCH 3/4] fixup: better messaging for the error when exporting schema and there's no analyzed schema --- packages/compass-schema/src/stores/schema-export-reducer.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/compass-schema/src/stores/schema-export-reducer.ts b/packages/compass-schema/src/stores/schema-export-reducer.ts index 49f9a8305a4..cea4d8cad58 100644 --- a/packages/compass-schema/src/stores/schema-export-reducer.ts +++ b/packages/compass-schema/src/stores/schema-export-reducer.ts @@ -238,7 +238,9 @@ export const changeExportSchemaFormat = ( try { const schemaAccessor = schemaAccessorRef.current; if (!schemaAccessor) { - throw new Error('No schema analysis available'); + throw new Error( + "No schema analysis available. Please analyze the collection's schema before exporting." + ); } exportedSchema = await getSchemaByFormat({ From f0d5c9813ab88af6d2df0b2e097cabff5cb3a302 Mon Sep 17 00:00:00 2001 From: Rhys Howell Date: Wed, 26 Feb 2025 13:35:15 -0500 Subject: [PATCH 4/4] fixup: show tooltip after closing new export flow as well --- .../src/components/compass-schema.tsx | 16 ++++++++++++++-- .../components/export-schema-legacy-modal.tsx | 8 +++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/compass-schema/src/components/compass-schema.tsx b/packages/compass-schema/src/components/compass-schema.tsx index 55359c028f8..0d16168e183 100644 --- a/packages/compass-schema/src/components/compass-schema.tsx +++ b/packages/compass-schema/src/components/compass-schema.tsx @@ -369,10 +369,17 @@ const PerformanceAdvisorBanner = () => { const Schema: React.FunctionComponent<{ analysisState: AnalysisState; + isExportSchemaModalOpen: boolean; schema: MongodbSchema | null; onStartAnalysis: () => Promise; onStopAnalysis: () => void; -}> = ({ analysisState, schema, onStartAnalysis, onStopAnalysis }) => { +}> = ({ + analysisState, + isExportSchemaModalOpen, + schema, + onStartAnalysis, + onStopAnalysis, +}) => { const onApplyClicked = useCallback(() => { void onStartAnalysis(); }, [onStartAnalysis]); @@ -395,7 +402,11 @@ const Schema: React.FunctionComponent<{ @@ -428,6 +439,7 @@ const Schema: React.FunctionComponent<{ export default connect( (state: RootState) => ({ analysisState: state.schemaAnalysis.analysisState, + isExportSchemaModalOpen: state.schemaExport.isOpen, schema: state.schemaAnalysis.schema, }), { diff --git a/packages/compass-schema/src/components/export-schema-legacy-modal.tsx b/packages/compass-schema/src/components/export-schema-legacy-modal.tsx index 28d67b93803..1dbcfc59b0a 100644 --- a/packages/compass-schema/src/components/export-schema-legacy-modal.tsx +++ b/packages/compass-schema/src/components/export-schema-legacy-modal.tsx @@ -491,8 +491,14 @@ const ExportSchemaLegacyModal: React.FunctionComponent<{ ]); const handleSwitchToNew = useCallback(() => { if (dontShowAgainChecked) stopShowingLegacyModal('export'); + setShowLegacyExportTooltip(true); onSwitchToSchemaExport(); - }, [onSwitchToSchemaExport, dontShowAgainChecked, stopShowingLegacyModal]); + }, [ + onSwitchToSchemaExport, + dontShowAgainChecked, + stopShowingLegacyModal, + setShowLegacyExportTooltip, + ]); return (