diff --git a/extension/src/setup/index.ts b/extension/src/setup/index.ts index d39e426b0d..941b909b1b 100644 --- a/extension/src/setup/index.ts +++ b/extension/src/setup/index.ts @@ -437,6 +437,7 @@ export class Setup pythonBinPath: getBinDisplayText(pythonBinPath), remoteList, sectionCollapsed: collectSectionCollapsed(this.focusedSection), + selfHostedStudioUrl: this.studio.getSelfHostedStudioUrl(), shareLiveToStudio: !!this.studio.getShareLiveToStudio() }) this.focusedSection = undefined diff --git a/extension/src/setup/studio.ts b/extension/src/setup/studio.ts index ea91c19cc2..17df0fa119 100644 --- a/extension/src/setup/studio.ts +++ b/extension/src/setup/studio.ts @@ -58,6 +58,11 @@ export class Studio extends Disposable { return this.studioUrl } + public getSelfHostedStudioUrl() { + const url = this.getStudioUrl() + return url === DEFAULT_STUDIO_URL ? null : url + } + public removeStudioAccessToken(dvcRoots: string[]) { return this.removeKeyFromConfig(dvcRoots, ConfigKey.STUDIO_TOKEN) } diff --git a/extension/src/setup/webview/contract.ts b/extension/src/setup/webview/contract.ts index bc0436d766..d6aefcabc5 100644 --- a/extension/src/setup/webview/contract.ts +++ b/extension/src/setup/webview/contract.ts @@ -22,6 +22,7 @@ export type SetupData = { pythonBinPath: string | undefined remoteList: RemoteList sectionCollapsed: typeof DEFAULT_SECTION_COLLAPSED | undefined + selfHostedStudioUrl: string | null shareLiveToStudio: boolean isAboveLatestTestedVersion: boolean | undefined } diff --git a/extension/src/setup/webview/messages.ts b/extension/src/setup/webview/messages.ts index 980a5129ad..a4f11f07fb 100644 --- a/extension/src/setup/webview/messages.ts +++ b/extension/src/setup/webview/messages.ts @@ -70,10 +70,14 @@ export class WebviewMessages { return commands.executeCommand( RegisteredCommands.ADD_STUDIO_ACCESS_TOKEN ) + case MessageFromWebviewType.SAVE_STUDIO_URL: + return commands.executeCommand(RegisteredCommands.UPDATE_STUDIO_URL) case MessageFromWebviewType.REMOVE_STUDIO_TOKEN: return commands.executeCommand( RegisteredCommands.REMOVE_STUDIO_ACCESS_TOKEN ) + case MessageFromWebviewType.REMOVE_STUDIO_URL: + return commands.executeCommand(RegisteredCommands.REMOVE_STUDIO_URL) case MessageFromWebviewType.SET_STUDIO_SHARE_EXPERIMENTS_LIVE: return this.updateStudioOffline(message.payload) case MessageFromWebviewType.REQUEST_STUDIO_TOKEN: diff --git a/extension/src/test/suite/setup/index.test.ts b/extension/src/test/suite/setup/index.test.ts index 8bf2b24484..843ea54a9f 100644 --- a/extension/src/test/suite/setup/index.test.ts +++ b/extension/src/test/suite/setup/index.test.ts @@ -395,6 +395,7 @@ suite('Setup Test Suite', () => { pythonBinPath: undefined, remoteList: undefined, sectionCollapsed: undefined, + selfHostedStudioUrl: null, shareLiveToStudio: false }) }).timeout(WEBVIEW_TEST_TIMEOUT) @@ -441,6 +442,7 @@ suite('Setup Test Suite', () => { pythonBinPath: undefined, remoteList: undefined, sectionCollapsed: undefined, + selfHostedStudioUrl: null, shareLiveToStudio: true }) }).timeout(WEBVIEW_TEST_TIMEOUT) @@ -495,6 +497,7 @@ suite('Setup Test Suite', () => { pythonBinPath: undefined, remoteList: undefined, sectionCollapsed: undefined, + selfHostedStudioUrl: null, shareLiveToStudio: true }) }).timeout(WEBVIEW_TEST_TIMEOUT) @@ -549,6 +552,7 @@ suite('Setup Test Suite', () => { pythonBinPath: undefined, remoteList: { [dvcDemoPath]: undefined }, sectionCollapsed: undefined, + selfHostedStudioUrl: null, shareLiveToStudio: true }) }).timeout(WEBVIEW_TEST_TIMEOUT) @@ -1332,6 +1336,68 @@ suite('Setup Test Suite', () => { ) }) + it("should handle a message from the webview to update the user's self hosted url", async () => { + const { setup, mockExecuteCommand } = buildSetup({ + disposer: disposable + }) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + stub(Setup.prototype as any, 'getCliCompatible').returns(true) + + const webview = await setup.showWebview() + await webview.isReady() + + const mockMessageReceived = getMessageReceivedEmitter(webview) + + const commandCalled = new Promise(resolve => + mockExecuteCommand.callsFake(() => { + resolve(undefined) + return Promise.resolve(undefined) + }) + ) + + mockMessageReceived.fire({ + type: MessageFromWebviewType.SAVE_STUDIO_URL + }) + + await commandCalled + + expect(mockExecuteCommand).to.be.calledWithExactly( + RegisteredCommands.UPDATE_STUDIO_URL + ) + }).timeout(WEBVIEW_TEST_TIMEOUT) + + it("should handle a message from the webview to remove the user's self hosted url", async () => { + const { setup, mockExecuteCommand } = buildSetup({ + disposer: disposable + }) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + stub(Setup.prototype as any, 'getCliCompatible').returns(true) + + const webview = await setup.showWebview() + await webview.isReady() + + const commandCalled = new Promise(resolve => + mockExecuteCommand.callsFake(() => { + resolve(undefined) + return Promise.resolve(undefined) + }) + ) + + const mockMessageReceived = getMessageReceivedEmitter(webview) + + mockMessageReceived.fire({ + type: MessageFromWebviewType.SAVE_STUDIO_URL + }) + + await commandCalled + + expect(mockExecuteCommand).to.be.calledWithExactly( + RegisteredCommands.UPDATE_STUDIO_URL + ) + }).timeout(WEBVIEW_TEST_TIMEOUT) + it('should check if experiments and dvc are setup', async () => { const { setup } = buildSetup({ disposer: disposable, diff --git a/extension/src/webview/contract.ts b/extension/src/webview/contract.ts index 11eee13d42..8e765436e5 100644 --- a/extension/src/webview/contract.ts +++ b/extension/src/webview/contract.ts @@ -47,6 +47,7 @@ export enum MessageFromWebviewType { RESIZE_PLOTS = 'resize-plots', REQUEST_STUDIO_TOKEN = 'request-studio-token', SAVE_STUDIO_TOKEN = 'save-studio-token', + SAVE_STUDIO_URL = 'save-studio-url', SET_COMPARISON_MULTI_PLOT_VALUE = 'update-comparison-multi-plot-value', SET_SMOOTH_PLOT_VALUE = 'update-smooth-plot-value', SHOW_EXPERIMENT_LOGS = 'show-experiment-logs', @@ -71,6 +72,7 @@ export enum MessageFromWebviewType { REMOTE_REMOVE = 'remote-remove', REMOVE_CUSTOM_PLOTS = 'remove-custom-plots', REMOVE_STUDIO_TOKEN = 'remove-studio-token', + REMOVE_STUDIO_URL = 'remove-studio-url', MODIFY_WORKSPACE_PARAMS_AND_QUEUE = 'modify-workspace-params-and-queue', MODIFY_WORKSPACE_PARAMS_AND_RUN = 'modify-workspace-params-and-run', MODIFY_WORKSPACE_PARAMS_RESET_AND_RUN = 'modify-workspace-params-reset-and-run', @@ -224,6 +226,7 @@ export type MessageFromWebview = type: MessageFromWebviewType.REMOVE_CUSTOM_PLOTS } | { type: MessageFromWebviewType.REMOVE_STUDIO_TOKEN } + | { type: MessageFromWebviewType.REMOVE_STUDIO_URL } | { type: MessageFromWebviewType.REORDER_PLOTS_COMPARISON payload: string[] @@ -291,6 +294,7 @@ export type MessageFromWebview = | { type: MessageFromWebviewType.UPGRADE_DVC } | { type: MessageFromWebviewType.SETUP_WORKSPACE } | { type: MessageFromWebviewType.SAVE_STUDIO_TOKEN } + | { type: MessageFromWebviewType.SAVE_STUDIO_URL } | { type: MessageFromWebviewType.REQUEST_STUDIO_TOKEN } | { type: MessageFromWebviewType.ADD_CONFIGURATION } | { type: MessageFromWebviewType.ZOOM_PLOT; payload?: string } diff --git a/webview/src/setup/components/App.test.tsx b/webview/src/setup/components/App.test.tsx index 6a61e995b6..c21777ab89 100644 --- a/webview/src/setup/components/App.test.tsx +++ b/webview/src/setup/components/App.test.tsx @@ -41,6 +41,7 @@ const DEFAULT_DATA = { pythonBinPath: undefined, remoteList: undefined, sectionCollapsed: undefined, + selfHostedStudioUrl: null, shareLiveToStudio: false } @@ -675,6 +676,65 @@ describe('App', () => { within(iconWrapper).getByTestId(TooltipIconType.WARNING) ).toBeInTheDocument() }) + + it('should show the self hosted url with actions to change it if the user has set one', () => { + const selfHostedUrl = 'https://studio.example.com' + renderApp({ selfHostedStudioUrl: selfHostedUrl }) + + const urlDetails = screen.getByTestId('studio-url-details') + + expect( + within(urlDetails).getByText('Self-Hosted Url:') + ).toBeInTheDocument() + expect(within(urlDetails).getByText(selfHostedUrl)).toBeInTheDocument() + + const updateUrlBtn = within(urlDetails).getByText('Update') + const removeUrlBtn = within(urlDetails).getByText('Remove') + + expect(updateUrlBtn).toBeInTheDocument() + expect(removeUrlBtn).toBeInTheDocument() + + mockPostMessage.mockClear() + fireEvent.click(updateUrlBtn) + + expect(mockPostMessage).toHaveBeenCalledTimes(1) + expect(mockPostMessage).toHaveBeenCalledWith({ + type: MessageFromWebviewType.SAVE_STUDIO_URL + }) + + mockPostMessage.mockClear() + fireEvent.click(removeUrlBtn) + + expect(mockPostMessage).toHaveBeenCalledTimes(1) + expect(mockPostMessage).toHaveBeenCalledWith({ + type: MessageFromWebviewType.REMOVE_STUDIO_URL + }) + }) + + it('should show the self hosted url with "Not found" with an action to add one if the user has not set one', () => { + renderApp() + + const urlDetails = screen.getByTestId('studio-url-details') + + expect( + within(urlDetails).getByText('Self-Hosted Url:') + ).toBeInTheDocument() + expect(within(urlDetails).getByText('Not found')).toBeInTheDocument() + + const addUrlBtn = within(urlDetails).getByText('Add Url') + + expect(addUrlBtn).toBeInTheDocument() + + mockPostMessage.mockClear() + fireEvent.click(addUrlBtn) + + expect(mockPostMessage).toHaveBeenCalledTimes(1) + expect(mockPostMessage).toHaveBeenCalledWith({ + type: MessageFromWebviewType.SAVE_STUDIO_URL + }) + + mockPostMessage.mockClear() + }) }) describe('Studio connected', () => { @@ -718,6 +778,24 @@ describe('App', () => { within(iconWrapper).getByTestId(TooltipIconType.PASSED) ).toBeInTheDocument() }) + + it('should show the self hosted url with actions to change it if the user has set one', () => { + const selfHostedUrl = 'https://studio.example.com' + renderApp({ isStudioConnected: true, selfHostedStudioUrl: selfHostedUrl }) + + const urlDetails = screen.getByTestId('studio-url-details') + + expect( + within(urlDetails).getByText('Self-Hosted Url:') + ).toBeInTheDocument() + expect(within(urlDetails).getByText(selfHostedUrl)).toBeInTheDocument() + + const updateUrlBtn = within(urlDetails).getByText('Update') + const removeUrlBtn = within(urlDetails).getByText('Remove') + + expect(updateUrlBtn).toBeInTheDocument() + expect(removeUrlBtn).toBeInTheDocument() + }) }) describe('focused section', () => { diff --git a/webview/src/setup/components/App.tsx b/webview/src/setup/components/App.tsx index 23b5ddd134..fe3e0c99e8 100644 --- a/webview/src/setup/components/App.tsx +++ b/webview/src/setup/components/App.tsx @@ -33,6 +33,7 @@ import { import { updateRemoteList } from '../state/remoteSlice' import { updateIsStudioConnected, + updateSelfHostedStudioUrl, updateShareLiveToStudio } from '../state/studioSlice' import { setStudioShareExperimentsLive } from '../util/messages' @@ -119,6 +120,9 @@ export const feedStore = ( case 'shareLiveToStudio': dispatch(updateShareLiveToStudio(data.data.shareLiveToStudio)) continue + case 'selfHostedStudioUrl': + dispatch(updateSelfHostedStudioUrl(data.data.selfHostedStudioUrl)) + continue case 'remoteList': dispatch(updateRemoteList(data.data.remoteList)) continue diff --git a/webview/src/setup/components/dvc/DvcEnvCommandRow.tsx b/webview/src/setup/components/dvc/DvcEnvCommandRow.tsx deleted file mode 100644 index 16efc6747b..0000000000 --- a/webview/src/setup/components/dvc/DvcEnvCommandRow.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import { DvcEnvInfoRow } from './DvcEnvInfoRow' -import styles from './styles.module.scss' -import { setupWorkspace, updatePythonEnvironment } from '../../util/messages' -import { SetupState } from '../../store' - -interface DvcEnvCommandRowProps { - command: string -} - -export const DvcEnvCommandRow: React.FC = ({ - command -}) => { - const isPythonExtensionUsed = useSelector( - (state: SetupState) => state.dvc.isPythonExtensionUsed - ) - const commandText = command || 'Not found' - const commandValue = ( - <> - {commandText} - - - {isPythonExtensionUsed && ( - <> - - - - )} - - - ) - - return -} diff --git a/webview/src/setup/components/dvc/DvcEnvDetails.tsx b/webview/src/setup/components/dvc/DvcEnvDetails.tsx index c315f689be..522009d81a 100644 --- a/webview/src/setup/components/dvc/DvcEnvDetails.tsx +++ b/webview/src/setup/components/dvc/DvcEnvDetails.tsx @@ -1,29 +1,51 @@ import React from 'react' +import { useSelector } from 'react-redux' import { DvcCliDetails } from 'dvc/src/setup/webview/contract' import { LATEST_TESTED_CLI_VERSION, MIN_CLI_VERSION } from 'dvc/src/cli/dvc/contract' -import { DvcEnvInfoRow } from './DvcEnvInfoRow' -import styles from './styles.module.scss' -import { DvcEnvCommandRow } from './DvcEnvCommandRow' +import { DetailsTable } from '../shared/DetailsTable' +import { + DetailsTableRow, + DetailsTableRowActions +} from '../shared/DetailsTableRow' +import { setupWorkspace, updatePythonEnvironment } from '../../util/messages' +import { SetupState } from '../../store' export const DvcEnvDetails: React.FC = ({ command, version }) => { - const versionText = ( - {`${ - version || 'Not found' - } (required ${MIN_CLI_VERSION} and above, tested with ${LATEST_TESTED_CLI_VERSION})`} + const isPythonExtensionUsed = useSelector( + (state: SetupState) => state.dvc.isPythonExtensionUsed ) + const commandRowActions: DetailsTableRowActions = [ + { onClick: setupWorkspace, text: 'Locate DVC' } + ] + + if (isPythonExtensionUsed) { + commandRowActions.push({ + onClick: updatePythonEnvironment, + text: 'Set Env' + }) + } return ( - - - {version && } - - -
+ + {version && ( + + )} + + ) } diff --git a/webview/src/setup/components/dvc/DvcEnvInfoRow.tsx b/webview/src/setup/components/dvc/DvcEnvInfoRow.tsx deleted file mode 100644 index 1d7b3bd27b..0000000000 --- a/webview/src/setup/components/dvc/DvcEnvInfoRow.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React, { ReactElement } from 'react' -import styles from './styles.module.scss' - -export const DvcEnvInfoRow: React.FC<{ - title: string - text: string | ReactElement -}> = ({ title, text }) => ( - - {title}: - {text} - -) diff --git a/webview/src/setup/components/dvc/styles.module.scss b/webview/src/setup/components/dvc/styles.module.scss index dd8db78424..a9b5e65b1f 100644 --- a/webview/src/setup/components/dvc/styles.module.scss +++ b/webview/src/setup/components/dvc/styles.module.scss @@ -1,37 +1,6 @@ @import '../../../shared/variables'; @import '../../../shared/mixins'; -.envDetails { - margin: 0 auto; - text-align: left; - margin-bottom: 1rem; -} - -.envDetailsKey, -.envDetailsValue { - padding: 5px; -} - -.envDetailsKey { - font-weight: bold; - white-space: nowrap; - vertical-align: top; -} - -.envDetailsValue { - padding-left: 50px; - display: flex; - flex-direction: column; -} - -.separator { - margin: 0 5px; - - &::before { - content: '|'; - } -} - .inlineWarningSvg { vertical-align: middle; fill: $warn-color; @@ -39,12 +8,6 @@ height: 16px; } -.buttonAsLink { - @extend %buttonAsLink; - - font-size: inherit; -} - .sideBySideButtons *:not(:first-child) { margin-left: 15px; } diff --git a/webview/src/setup/components/shared/DetailsTable.tsx b/webview/src/setup/components/shared/DetailsTable.tsx new file mode 100644 index 0000000000..87cabd97a3 --- /dev/null +++ b/webview/src/setup/components/shared/DetailsTable.tsx @@ -0,0 +1,14 @@ +import React, { PropsWithChildren } from 'react' + +import styles from './styles.module.scss' + +export const DetailsTable: React.FC> = ({ + children, + testId +}) => { + return ( + + {children} +
+ ) +} diff --git a/webview/src/setup/components/shared/DetailsTableRow.tsx b/webview/src/setup/components/shared/DetailsTableRow.tsx new file mode 100644 index 0000000000..17f33ee241 --- /dev/null +++ b/webview/src/setup/components/shared/DetailsTableRow.tsx @@ -0,0 +1,37 @@ +import React from 'react' +import styles from './styles.module.scss' + +export type DetailsTableRowActions = { text: string; onClick: () => void }[] + +type DetailsTableRowProps = { + title: string + text: string + actions?: DetailsTableRowActions +} + +export const DetailsTableRow: React.FC = ({ + title, + text, + actions +}) => { + return ( + + {title}: + + {text} + {actions && ( + + {actions.map(({ text, onClick }, index) => ( + + {index > 0 && } + + + ))} + + )} + + + ) +} diff --git a/webview/src/setup/components/shared/styles.module.scss b/webview/src/setup/components/shared/styles.module.scss index 1e203d451d..abd780c708 100644 --- a/webview/src/setup/components/shared/styles.module.scss +++ b/webview/src/setup/components/shared/styles.module.scss @@ -1,4 +1,5 @@ @import '../../../shared/variables'; +@import '../../../shared/mixins'; .codeBlock { text-align: left; @@ -19,3 +20,35 @@ fill: $accent-color; margin-bottom: -3px; } + +.detailsTable { + margin: 0 auto; + text-align: left; + margin-bottom: 1rem; +} + +.detailsTableKey { + font-weight: bold; + white-space: nowrap; + vertical-align: top; +} + +.detailsTableValue { + padding-left: 50px; + display: flex; + flex-direction: column; +} + +.buttonAsLink { + @extend %buttonAsLink; + + font-size: inherit; +} + +.separator { + margin: 0 5px; + + &::before { + content: '|'; + } +} diff --git a/webview/src/setup/components/studio/Connect.tsx b/webview/src/setup/components/studio/Connect.tsx index 2f89aebc06..a094c8444d 100644 --- a/webview/src/setup/components/studio/Connect.tsx +++ b/webview/src/setup/components/studio/Connect.tsx @@ -1,16 +1,17 @@ -import React from 'react' +import React, { PropsWithChildren } from 'react' import { DEFAULT_STUDIO_URL } from 'dvc/src/setup/webview/contract' import { EmptyState } from '../../../shared/components/emptyState/EmptyState' import { requestStudioToken, saveStudioToken } from '../../util/messages' import { Button } from '../../../shared/components/button/Button' -export const Connect: React.FC = () => { +export const Connect: React.FC = ({ children }) => { return (

Connect to Studio

+ {children}

Share experiments and plots with collaborators directly from your IDE. Start sending data with an{' '} diff --git a/webview/src/setup/components/studio/Settings.tsx b/webview/src/setup/components/studio/Settings.tsx index 46bf7acd8c..5392f27df9 100644 --- a/webview/src/setup/components/studio/Settings.tsx +++ b/webview/src/setup/components/studio/Settings.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { PropsWithChildren } from 'react' import { useSelector } from 'react-redux' import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react' import styles from './styles.module.scss' @@ -7,9 +7,11 @@ import { EmptyState } from '../../../shared/components/emptyState/EmptyState' import { Button } from '../../../shared/components/button/Button' import { SetupState } from '../../store' -export const Settings: React.FC<{ - setShareLiveToStudio: (shareLiveToStudio: boolean) => void -}> = ({ setShareLiveToStudio }) => { +export const Settings: React.FC< + PropsWithChildren<{ + setShareLiveToStudio: (shareLiveToStudio: boolean) => void + }> +> = ({ children, setShareLiveToStudio }) => { const shareLiveToStudio = useSelector( (state: SetupState) => state.studio.shareLiveToStudio ) @@ -18,6 +20,7 @@ export const Settings: React.FC<{

Studio Settings

+ {children}
void }> = ({ cliCompatible, setShareLiveToStudio }) => { - const { isStudioConnected } = useSelector((state: SetupState) => state.studio) + const { isStudioConnected, selfHostedStudioUrl } = useSelector( + (state: SetupState) => state.studio + ) if (!cliCompatible) { return ( @@ -22,9 +27,32 @@ export const Studio: React.FC<{ ) } + const children = ( + + + + ) + return isStudioConnected ? ( - + {children} ) : ( - + {children} ) } diff --git a/webview/src/setup/state/dvcSlice.ts b/webview/src/setup/state/dvcSlice.ts index 3368e8604e..95417ba051 100644 --- a/webview/src/setup/state/dvcSlice.ts +++ b/webview/src/setup/state/dvcSlice.ts @@ -8,6 +8,7 @@ export type DvcState = Omit< | 'needsGitCommit' | 'sectionCollapsed' | 'shareLiveToStudio' + | 'selfHostedStudioUrl' > export const dvcInitialState: DvcState = { diff --git a/webview/src/setup/state/studioSlice.ts b/webview/src/setup/state/studioSlice.ts index f2ce1628ce..89d3188fb0 100644 --- a/webview/src/setup/state/studioSlice.ts +++ b/webview/src/setup/state/studioSlice.ts @@ -3,11 +3,12 @@ import { SetupData } from 'dvc/src/setup/webview/contract' export type StudioState = Pick< SetupData, - 'shareLiveToStudio' | 'isStudioConnected' + 'shareLiveToStudio' | 'isStudioConnected' | 'selfHostedStudioUrl' > export const studioInitialState: StudioState = { isStudioConnected: false, + selfHostedStudioUrl: null, shareLiveToStudio: false } @@ -18,13 +19,22 @@ export const studioSlice = createSlice({ updateIsStudioConnected: (state, action: PayloadAction) => { state.isStudioConnected = action.payload }, + updateSelfHostedStudioUrl: ( + state, + action: PayloadAction + ) => { + state.selfHostedStudioUrl = action.payload + }, updateShareLiveToStudio: (state, action: PayloadAction) => { state.shareLiveToStudio = action.payload } } }) -export const { updateIsStudioConnected, updateShareLiveToStudio } = - studioSlice.actions +export const { + updateIsStudioConnected, + updateShareLiveToStudio, + updateSelfHostedStudioUrl +} = studioSlice.actions export default studioSlice.reducer diff --git a/webview/src/setup/util/messages.ts b/webview/src/setup/util/messages.ts index 58d6e901ff..04333aa5ad 100644 --- a/webview/src/setup/util/messages.ts +++ b/webview/src/setup/util/messages.ts @@ -48,6 +48,9 @@ export const showExperiments = () => { export const saveStudioToken = () => sendMessage({ type: MessageFromWebviewType.SAVE_STUDIO_TOKEN }) +export const saveStudioUrl = () => + sendMessage({ type: MessageFromWebviewType.SAVE_STUDIO_URL }) + export const setStudioShareExperimentsLive = (shouldShareLive: boolean) => sendMessage({ payload: shouldShareLive, @@ -57,6 +60,9 @@ export const setStudioShareExperimentsLive = (shouldShareLive: boolean) => export const removeStudioToken = () => sendMessage({ type: MessageFromWebviewType.REMOVE_STUDIO_TOKEN }) +export const removeStudioUrl = () => + sendMessage({ type: MessageFromWebviewType.REMOVE_STUDIO_URL }) + export const requestStudioToken = () => sendMessage({ type: MessageFromWebviewType.REQUEST_STUDIO_TOKEN }) diff --git a/webview/src/stories/Setup.stories.tsx b/webview/src/stories/Setup.stories.tsx index 9c10a44791..75db364aca 100644 --- a/webview/src/stories/Setup.stories.tsx +++ b/webview/src/stories/Setup.stories.tsx @@ -32,6 +32,7 @@ const DEFAULT_DATA: SetupData = { [SetupSection.REMOTES]: false, [SetupSection.STUDIO]: true }, + selfHostedStudioUrl: null, shareLiveToStudio: false } @@ -99,6 +100,11 @@ CompletedConnected.args = getUpdatedArgs({ shareLiveToStudio: true }) +export const WithSelfHostedStudioUrl = Template.bind({}) +WithSelfHostedStudioUrl.args = getUpdatedArgs({ + selfHostedStudioUrl: 'https://studio.example.com' +}) + export const CLIPythonNotFound = Template.bind({}) CLIPythonNotFound.args = getUpdatedArgs({ cliCompatible: undefined,