diff --git a/playwright/github-pr-drawer.spec.ts b/playwright/github-pr-drawer.spec.ts index 224baae..4d7cd94 100644 --- a/playwright/github-pr-drawer.spec.ts +++ b/playwright/github-pr-drawer.spec.ts @@ -1133,7 +1133,7 @@ test('Reloaded active PR context from URL metadata keeps Push mode and status re expect(upsertRequests[1]?.body.message).toBe(defaultCommitMessage) }) -test('Reloaded active PR context syncs editor content from GitHub branch', async ({ +test('Reloaded active PR context syncs editor content from GitHub branch and restores style mode', async ({ page, }) => { const remoteComponentSource = 'export const App = () =>
Synced from PR
' @@ -1237,6 +1237,7 @@ test('Reloaded active PR context syncs editor content from GitHub branch', async componentFilePath: 'examples/component/App.tsx', stylesFilePath: 'examples/styles/app.css', renderMode: 'react', + styleMode: 'sass', baseBranch: 'main', headBranch: 'develop/open-pr-test', prTitle: 'Existing PR context from storage', @@ -1249,6 +1250,7 @@ test('Reloaded active PR context syncs editor content from GitHub branch', async await connectByotWithSingleRepo(page) await expect(page.getByLabel('Render mode')).toHaveValue('react') + await expect(page.getByLabel('Style mode')).toHaveValue('sass') await expect .poll(async () => @@ -1269,6 +1271,73 @@ test('Reloaded active PR context syncs editor content from GitHub branch', async }) }) +test('Reloaded active PR context falls back to css style mode for unsupported value', async ({ + page, +}) => { + await page.route('https://api.github.com/user/repos**', async route => { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify([ + { + id: 11, + owner: { login: 'knightedcodemonkey' }, + name: 'develop', + full_name: 'knightedcodemonkey/develop', + default_branch: 'main', + permissions: { push: true }, + }, + ]), + }) + }) + + await mockRepositoryBranches(page, { + 'knightedcodemonkey/develop': ['main', 'release', 'develop/open-pr-test'], + }) + + await page.route( + 'https://api.github.com/repos/knightedcodemonkey/develop/pulls/2', + async route => { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + number: 2, + state: 'open', + title: 'Existing PR context from storage', + html_url: 'https://github.com/knightedcodemonkey/develop/pull/2', + head: { ref: 'develop/open-pr-test' }, + base: { ref: 'main' }, + }), + }) + }, + ) + + await waitForAppReady(page, `${appEntryPath}?feature-ai=true`) + + await page.evaluate(() => { + localStorage.setItem( + 'knighted:develop:github-pr-config:knightedcodemonkey/develop', + JSON.stringify({ + componentFilePath: 'examples/component/App.tsx', + stylesFilePath: 'examples/styles/app.css', + renderMode: 'react', + styleMode: 'scss', + baseBranch: 'main', + headBranch: 'develop/open-pr-test', + prTitle: 'Existing PR context from storage', + prBody: 'Saved body', + isActivePr: true, + pullRequestUrl: 'https://github.com/knightedcodemonkey/develop/pull/2', + }), + ) + }) + + await connectByotWithSingleRepo(page) + await expect(page.getByLabel('Render mode')).toHaveValue('react') + await expect(page.getByLabel('Style mode')).toHaveValue('css') +}) + test('Open PR drawer validates unsafe filepaths', async ({ page }) => { await waitForAppReady(page, `${appEntryPath}?feature-ai=true`) await connectByotWithSingleRepo(page) diff --git a/playwright/helpers/app-test-helpers.ts b/playwright/helpers/app-test-helpers.ts index 5af299b..fd9c9af 100644 --- a/playwright/helpers/app-test-helpers.ts +++ b/playwright/helpers/app-test-helpers.ts @@ -226,8 +226,6 @@ export const connectByotWithSingleRepo = async (page: Page) => { const repoSelect = page.getByLabel('Pull request repository') await expect(repoSelect).toHaveValue('knightedcodemonkey/develop') - await expect(repoSelect).toHaveValue('knightedcodemonkey/develop') - await expect( page.getByRole('button', { name: /Open pull request|Push commit to active pull request branch/, diff --git a/src/app.js b/src/app.js index 6cb7d22..75051b7 100644 --- a/src/app.js +++ b/src/app.js @@ -815,6 +815,7 @@ prDrawerController = createGitHubPrDrawer({ getStylesSource: () => getCssSource(), getTopLevelDeclarations, getRenderMode: () => renderMode.value, + getStyleMode: () => styleMode.value, getDrawerSide: () => { const layout = getCurrentLayout() return layout === 'preview-left' ? 'left' : 'right' @@ -872,6 +873,9 @@ prDrawerController = createGitHubPrDrawer({ onRestoreRenderMode: mode => { applyRenderMode({ mode, fromActivePrContext: true }) }, + onRestoreStyleMode: mode => { + applyStyleMode({ mode }) + }, }) prDrawerController.setToken(githubAiContextState.token) @@ -926,6 +930,13 @@ const getStyleEditorLanguage = mode => { return 'css' } +const normalizeStyleMode = mode => { + if (mode === 'module') return 'module' + if (mode === 'less') return 'less' + if (mode === 'sass') return 'sass' + return 'css' +} + const createEditorHost = textarea => { const host = document.createElement('div') host.className = 'editor-host' @@ -1437,21 +1448,32 @@ function applyRenderMode({ mode, fromActivePrContext = false }) { maybeRender() } -renderMode.addEventListener('change', () => { - applyRenderMode({ mode: renderMode.value }) -}) -styleMode.addEventListener('change', () => { +function applyStyleMode({ mode }) { + const nextMode = normalizeStyleMode(mode) + + if (styleMode.value !== nextMode) { + styleMode.value = nextMode + } + resetDiagnosticsFlow() if (cssCodeEditor) { suppressEditorChangeSideEffects = true try { - cssCodeEditor.setLanguage(getStyleEditorLanguage(styleMode.value)) + cssCodeEditor.setLanguage(getStyleEditorLanguage(nextMode)) } finally { suppressEditorChangeSideEffects = false } } + maybeRender() +} + +renderMode.addEventListener('change', () => { + applyRenderMode({ mode: renderMode.value }) +}) +styleMode.addEventListener('change', () => { + applyStyleMode({ mode: styleMode.value }) }) shadowToggle.addEventListener('change', maybeRender) autoRenderToggle.addEventListener('change', () => { diff --git a/src/modules/github-pr-drawer.js b/src/modules/github-pr-drawer.js index 2ad158f..30b9f21 100644 --- a/src/modules/github-pr-drawer.js +++ b/src/modules/github-pr-drawer.js @@ -25,12 +25,18 @@ const defaultPrConfig = { const defaultCommitMessage = 'chore: sync editor updates from @knighted/develop' const supportedRenderModes = new Set(['dom', 'react']) +const supportedStyleModes = new Set(['css', 'module', 'less', 'sass']) const normalizeRenderMode = value => { const mode = toSafeText(value).toLowerCase() return supportedRenderModes.has(mode) ? mode : 'dom' } +const normalizeStyleMode = value => { + const mode = toSafeText(value).toLowerCase() + return supportedStyleModes.has(mode) ? mode : 'css' +} + const getRepositoryPrConfigStorageKey = repositoryFullName => `${prConfigStoragePrefix}${repositoryFullName}` @@ -133,6 +139,7 @@ const getActiveRepositoryPrContext = repositoryFullName => { componentFilePath: componentFilePath.value, stylesFilePath: stylesFilePath.value, renderMode: normalizeRenderMode(savedConfig.renderMode), + styleMode: normalizeStyleMode(savedConfig.styleMode), prTitle, prBody: typeof savedConfig.prBody === 'string' ? savedConfig.prBody : '', baseBranch, @@ -395,6 +402,7 @@ export const createGitHubPrDrawer = ({ getStylesSource, getTopLevelDeclarations, getRenderMode, + getStyleMode, getDrawerSide, confirmBeforeSubmit, onPullRequestOpened, @@ -402,6 +410,7 @@ export const createGitHubPrDrawer = ({ onActivePrContextChange, onSyncActivePrEditorContent, onRestoreRenderMode, + onRestoreStyleMode, }) => { if (!featureEnabled) { toggleButton?.setAttribute('hidden', '') @@ -548,6 +557,19 @@ export const createGitHubPrDrawer = ({ onRestoreRenderMode(mode) } + const emitStyleModeRestore = activeContext => { + if (typeof onRestoreStyleMode !== 'function') { + return + } + + if (!activeContext) { + return + } + + const mode = normalizeStyleMode(activeContext?.styleMode) + onRestoreStyleMode(mode) + } + const emitActivePrContextChange = () => { if (typeof onActivePrContextChange !== 'function') { return @@ -556,6 +578,7 @@ export const createGitHubPrDrawer = ({ const activeContext = getCurrentActivePrContext() onActivePrContextChange(activeContext) emitRenderModeRestore(activeContext) + emitStyleModeRestore(activeContext) } const setStatus = (text, level = 'neutral') => { @@ -762,6 +785,7 @@ export const createGitHubPrDrawer = ({ ...savedConfig, isActivePr: true, renderMode: normalizeRenderMode(savedConfig.renderMode), + styleMode: normalizeStyleMode(savedConfig.styleMode), headBranch: nextHeadBranch, baseBranch: nextBaseBranch, pullRequestNumber: resolvedPullRequest.number, @@ -1061,6 +1085,7 @@ export const createGitHubPrDrawer = ({ const values = getFormValues() const currentRenderMode = normalizeRenderMode(getRenderMode?.()) + const currentStyleMode = normalizeStyleMode(getStyleMode?.()) const existingConfig = readRepositoryPrConfig(repositoryFullName) const isActivePr = existingConfig?.isActivePr === true @@ -1078,6 +1103,7 @@ export const createGitHubPrDrawer = ({ ? existingConfig.stylesFilePath : values.stylesFilePath, renderMode: currentRenderMode, + styleMode: currentStyleMode, isActivePr: true, pullRequestNumber: existingConfig?.pullRequestNumber, pullRequestUrl: existingConfig?.pullRequestUrl, @@ -1099,6 +1125,7 @@ export const createGitHubPrDrawer = ({ prTitle: values.prTitle, prBody: values.prBody, renderMode: currentRenderMode, + styleMode: currentStyleMode, isActivePr: false, pullRequestNumber: existingConfig?.pullRequestNumber, pullRequestUrl: existingConfig?.pullRequestUrl, @@ -1199,6 +1226,7 @@ export const createGitHubPrDrawer = ({ : '' : values.prBody const currentRenderMode = normalizeRenderMode(getRenderMode?.()) + const currentStyleMode = normalizeStyleMode(getStyleMode?.()) const targetComponentPathValue = isPushCommitMode ? activeContext?.componentFilePath : values.componentFilePath @@ -1348,6 +1376,7 @@ export const createGitHubPrDrawer = ({ componentFilePath: componentPathValidation.value, stylesFilePath: stylesPathValidation.value, renderMode: currentRenderMode, + styleMode: currentStyleMode, baseBranch: targetBaseBranch, headBranch: targetHeadBranch, prTitle: targetPrTitle,