Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 70 additions & 1 deletion playwright/github-pr-drawer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => <main>Synced from PR</main>'
Expand Down Expand Up @@ -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',
Expand All @@ -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 () =>
Expand All @@ -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)
Expand Down
2 changes: 0 additions & 2 deletions playwright/helpers/app-test-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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/,
Expand Down
32 changes: 27 additions & 5 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -872,6 +873,9 @@ prDrawerController = createGitHubPrDrawer({
onRestoreRenderMode: mode => {
applyRenderMode({ mode, fromActivePrContext: true })
},
onRestoreStyleMode: mode => {
applyStyleMode({ mode })
},
})

prDrawerController.setToken(githubAiContextState.token)
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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', () => {
Expand Down
29 changes: 29 additions & 0 deletions src/modules/github-pr-drawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -395,13 +402,15 @@ export const createGitHubPrDrawer = ({
getStylesSource,
getTopLevelDeclarations,
getRenderMode,
getStyleMode,
getDrawerSide,
confirmBeforeSubmit,
onPullRequestOpened,
onPullRequestCommitPushed,
onActivePrContextChange,
onSyncActivePrEditorContent,
onRestoreRenderMode,
onRestoreStyleMode,
}) => {
if (!featureEnabled) {
toggleButton?.setAttribute('hidden', '')
Expand Down Expand Up @@ -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
Expand All @@ -556,6 +578,7 @@ export const createGitHubPrDrawer = ({
const activeContext = getCurrentActivePrContext()
onActivePrContextChange(activeContext)
emitRenderModeRestore(activeContext)
emitStyleModeRestore(activeContext)
}

const setStatus = (text, level = 'neutral') => {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand All @@ -1078,6 +1103,7 @@ export const createGitHubPrDrawer = ({
? existingConfig.stylesFilePath
: values.stylesFilePath,
renderMode: currentRenderMode,
styleMode: currentStyleMode,
isActivePr: true,
pullRequestNumber: existingConfig?.pullRequestNumber,
pullRequestUrl: existingConfig?.pullRequestUrl,
Expand All @@ -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,
Expand Down Expand Up @@ -1199,6 +1226,7 @@ export const createGitHubPrDrawer = ({
: ''
: values.prBody
const currentRenderMode = normalizeRenderMode(getRenderMode?.())
const currentStyleMode = normalizeStyleMode(getStyleMode?.())
const targetComponentPathValue = isPushCommitMode
? activeContext?.componentFilePath
: values.componentFilePath
Expand Down Expand Up @@ -1348,6 +1376,7 @@ export const createGitHubPrDrawer = ({
componentFilePath: componentPathValidation.value,
stylesFilePath: stylesPathValidation.value,
renderMode: currentRenderMode,
styleMode: currentStyleMode,
baseBranch: targetBaseBranch,
headBranch: targetHeadBranch,
prTitle: targetPrTitle,
Expand Down
Loading