Proper command bar UI support for optional args (#7506)
* WIP: Add bidirectional args to point-and-click Extrude Will eventually close #7495 * Wire up edit flow for symmetric * Show skip true args in header in review phase * Add bidirectionalLength * Make currentArg always part of header * WIP * Add twistAng * Proper optional args line in review * Labels in progress button and option arg section heading * Clean up extrude specific changes * More UI polish * Remove options bool icon * Fix labels for tests * Upgrade e2e tests to cmdBar fixtures with fixes * More fixes * Fixed up more tests related to sweep behavior change * Fix nodeToEdit not having hidden: true on Shell * Add typecheck * WIP: footer buttons * back to reg width * Clean up * Clean up * Fix tests and remove label * Refactor * Fix offset plane test * Add CommandBarDivider * Fix step back * Add comment * Fix it, thanks bot * Clean up and inline optional heading * Little case tweak * Update src/components/CommandBar/CommandBarReview.tsx Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> * Rename to CommandBarHeaderFooter --------- Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
This commit is contained in:
@ -307,7 +307,7 @@ test.describe('Command bar tests', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const continueButton = page.getByRole('button', { name: 'Continue' })
|
const continueButton = page.getByRole('button', { name: 'Continue' })
|
||||||
const submitButton = page.getByRole('button', { name: 'Submit command' })
|
const submitButton = page.getByTestId('command-bar-submit')
|
||||||
await continueButton.click()
|
await continueButton.click()
|
||||||
|
|
||||||
// Review step and argument hotkeys
|
// Review step and argument hotkeys
|
||||||
|
@ -54,9 +54,7 @@ test(
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
const submitButton = page.getByText('Confirm Export')
|
await cmdBar.submit()
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Expect it to succeed
|
// Expect it to succeed
|
||||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||||
@ -119,9 +117,7 @@ test(
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
const submitButton = page.getByText('Confirm Export')
|
await cmdBar.submit()
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||||
|
@ -118,15 +118,11 @@ export class CmdBarFixture {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const arrowButton = this.page.getByRole('button', {
|
const arrowButton = this.page.getByTestId('command-bar-continue')
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
if (await arrowButton.isVisible()) {
|
if (await arrowButton.isVisible()) {
|
||||||
await arrowButton.click()
|
await this.continue()
|
||||||
} else {
|
} else {
|
||||||
await this.page
|
await this.submit()
|
||||||
.getByRole('button', { name: 'checkmark Submit command' })
|
|
||||||
.click()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1083,14 +1083,13 @@ openSketch = startSketchOn(XY)
|
|||||||
cmdBar,
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 700, y: 150 }
|
const testPoint = { x: 700, y: 200 }
|
||||||
|
// TODO: replace the testPoint selection with a feature tree click once that's supported #7544
|
||||||
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
const expectedOutput = `plane001 = offsetPlane(XZ, offset = 5)`
|
const expectedOutput = `plane001 = offsetPlane(XZ, offset = 5)`
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
// FIXME: Since there is no KCL code loaded. We need to wait for the scene to load before we continue.
|
await scene.settled(cmdBar)
|
||||||
// The engine may not be connected
|
|
||||||
await page.waitForTimeout(15000)
|
|
||||||
|
|
||||||
await test.step(`Look for the blue of the XZ plane`, async () => {
|
await test.step(`Look for the blue of the XZ plane`, async () => {
|
||||||
//await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME
|
//await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME
|
||||||
@ -1829,7 +1828,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
|||||||
currentArgKey: 'sketches',
|
currentArgKey: 'sketches',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '',
|
Profiles: '',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -1843,7 +1841,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
|||||||
currentArgKey: 'path',
|
currentArgKey: 'path',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -1856,7 +1853,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
|||||||
currentArgKey: 'path',
|
currentArgKey: 'path',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -1869,7 +1865,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
|||||||
headerArguments: {
|
headerArguments: {
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '1 segment',
|
Path: '1 segment',
|
||||||
Sectional: '',
|
|
||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
})
|
})
|
||||||
@ -1894,6 +1889,9 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
|||||||
0
|
0
|
||||||
)
|
)
|
||||||
await operationButton.dblclick({ button: 'left' })
|
await operationButton.dblclick({ button: 'left' })
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'sectional', exact: false })
|
||||||
|
.click()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
currentArgKey: 'sectional',
|
currentArgKey: 'sectional',
|
||||||
@ -1971,7 +1969,6 @@ profile001 = ${circleCode}`
|
|||||||
currentArgKey: 'sketches',
|
currentArgKey: 'sketches',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '',
|
Profiles: '',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -1986,7 +1983,6 @@ profile001 = ${circleCode}`
|
|||||||
currentArgKey: 'path',
|
currentArgKey: 'path',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -2000,7 +1996,6 @@ profile001 = ${circleCode}`
|
|||||||
currentArgKey: 'path',
|
currentArgKey: 'path',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -2013,7 +2008,6 @@ profile001 = ${circleCode}`
|
|||||||
headerArguments: {
|
headerArguments: {
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '1 helix',
|
Path: '1 helix',
|
||||||
Sectional: '',
|
|
||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
})
|
})
|
||||||
@ -4734,7 +4728,6 @@ path001 = startProfile(sketch001, at = [0, 0])
|
|||||||
headerArguments: {
|
headerArguments: {
|
||||||
Profiles: '',
|
Profiles: '',
|
||||||
Path: '',
|
Path: '',
|
||||||
Sectional: '',
|
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'Profiles',
|
highlightedHeaderArg: 'Profiles',
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
@ -4747,7 +4740,6 @@ path001 = startProfile(sketch001, at = [0, 0])
|
|||||||
headerArguments: {
|
headerArguments: {
|
||||||
Profiles: '2 profiles',
|
Profiles: '2 profiles',
|
||||||
Path: '',
|
Path: '',
|
||||||
Sectional: '',
|
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'path',
|
highlightedHeaderArg: 'path',
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
@ -4760,7 +4752,6 @@ path001 = startProfile(sketch001, at = [0, 0])
|
|||||||
headerArguments: {
|
headerArguments: {
|
||||||
Profiles: '2 profiles',
|
Profiles: '2 profiles',
|
||||||
Path: '1 segment',
|
Path: '1 segment',
|
||||||
Sectional: '',
|
|
||||||
},
|
},
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
})
|
})
|
||||||
|
@ -475,6 +475,7 @@ test.describe('Can export from electron app', () => {
|
|||||||
},
|
},
|
||||||
tronApp.projectDirName,
|
tronApp.projectDirName,
|
||||||
page,
|
page,
|
||||||
|
cmdBar,
|
||||||
method
|
method
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -779,9 +780,6 @@ test.describe(`Project management commands`, () => {
|
|||||||
const commandContinueButton = page.getByRole('button', {
|
const commandContinueButton = page.getByRole('button', {
|
||||||
name: 'Continue',
|
name: 'Continue',
|
||||||
})
|
})
|
||||||
const commandSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'Submit command',
|
|
||||||
})
|
|
||||||
const toastMessage = page.getByText(`Successfully renamed`)
|
const toastMessage = page.getByText(`Successfully renamed`)
|
||||||
|
|
||||||
await test.step(`Setup`, async () => {
|
await test.step(`Setup`, async () => {
|
||||||
@ -800,8 +798,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
await expect(commandContinueButton).toBeVisible()
|
await expect(commandContinueButton).toBeVisible()
|
||||||
await commandContinueButton.click()
|
await commandContinueButton.click()
|
||||||
|
|
||||||
await expect(commandSubmitButton).toBeVisible()
|
await cmdBar.submit()
|
||||||
await commandSubmitButton.click()
|
|
||||||
|
|
||||||
await expect(toastMessage).toBeVisible()
|
await expect(toastMessage).toBeVisible()
|
||||||
})
|
})
|
||||||
@ -837,9 +834,6 @@ test.describe(`Project management commands`, () => {
|
|||||||
})
|
})
|
||||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||||
const commandWarning = page.getByText('Are you sure you want to delete?')
|
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||||
const commandSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'Submit command',
|
|
||||||
})
|
|
||||||
const toastMessage = page.getByText(`Successfully deleted`)
|
const toastMessage = page.getByText(`Successfully deleted`)
|
||||||
const noProjectsMessage = page.getByText('No projects found')
|
const noProjectsMessage = page.getByText('No projects found')
|
||||||
|
|
||||||
@ -859,8 +853,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
await projectNameOption.click()
|
await projectNameOption.click()
|
||||||
|
|
||||||
await expect(commandWarning).toBeVisible()
|
await expect(commandWarning).toBeVisible()
|
||||||
await expect(commandSubmitButton).toBeVisible()
|
await cmdBar.submit()
|
||||||
await commandSubmitButton.click()
|
|
||||||
|
|
||||||
await expect(toastMessage).toBeVisible()
|
await expect(toastMessage).toBeVisible()
|
||||||
})
|
})
|
||||||
@ -894,9 +887,6 @@ test.describe(`Project management commands`, () => {
|
|||||||
const commandContinueButton = page.getByRole('button', {
|
const commandContinueButton = page.getByRole('button', {
|
||||||
name: 'Continue',
|
name: 'Continue',
|
||||||
})
|
})
|
||||||
const commandSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'Submit command',
|
|
||||||
})
|
|
||||||
const toastMessage = page.getByText(`Successfully renamed`)
|
const toastMessage = page.getByText(`Successfully renamed`)
|
||||||
|
|
||||||
await test.step(`Setup`, async () => {
|
await test.step(`Setup`, async () => {
|
||||||
@ -914,8 +904,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
await expect(commandContinueButton).toBeVisible()
|
await expect(commandContinueButton).toBeVisible()
|
||||||
await commandContinueButton.click()
|
await commandContinueButton.click()
|
||||||
|
|
||||||
await expect(commandSubmitButton).toBeVisible()
|
await cmdBar.submit()
|
||||||
await commandSubmitButton.click()
|
|
||||||
|
|
||||||
await expect(toastMessage).toBeVisible()
|
await expect(toastMessage).toBeVisible()
|
||||||
})
|
})
|
||||||
@ -949,9 +938,6 @@ test.describe(`Project management commands`, () => {
|
|||||||
})
|
})
|
||||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||||
const commandWarning = page.getByText('Are you sure you want to delete?')
|
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||||
const commandSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'Submit command',
|
|
||||||
})
|
|
||||||
const toastMessage = page.getByText(`Successfully deleted`)
|
const toastMessage = page.getByText(`Successfully deleted`)
|
||||||
const noProjectsMessage = page.getByText('No projects found')
|
const noProjectsMessage = page.getByText('No projects found')
|
||||||
|
|
||||||
@ -967,8 +953,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
await projectNameOption.click()
|
await projectNameOption.click()
|
||||||
|
|
||||||
await expect(commandWarning).toBeVisible()
|
await expect(commandWarning).toBeVisible()
|
||||||
await expect(commandSubmitButton).toBeVisible()
|
await cmdBar.submit()
|
||||||
await commandSubmitButton.click()
|
|
||||||
|
|
||||||
await expect(toastMessage).toBeVisible()
|
await expect(toastMessage).toBeVisible()
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { bracket } from '@e2e/playwright/fixtures/bracket'
|
import { bracket } from '@e2e/playwright/fixtures/bracket'
|
||||||
import type { Page } from '@playwright/test'
|
import type { Page } from '@playwright/test'
|
||||||
|
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||||
import { reportRejection } from '@src/lib/trap'
|
import { reportRejection } from '@src/lib/trap'
|
||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
|
|
||||||
@ -421,10 +422,7 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
const submitButton = page.getByText('Confirm Export')
|
await cmdBar.submit()
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Find the toast.
|
// Find the toast.
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
@ -461,8 +459,7 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
await expect(submitButton).toBeVisible()
|
await cmdBar.submit()
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Find the toast.
|
// Find the toast.
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
@ -482,6 +479,7 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
test('ensure you CAN export while an export is already going', async ({
|
test('ensure you CAN export while an export is already going', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await test.step('Set up the code and durations', async () => {
|
await test.step('Set up the code and durations', async () => {
|
||||||
@ -516,11 +514,11 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
const successToastMessage = page.getByText(`Exported successfully`)
|
const successToastMessage = page.getByText(`Exported successfully`)
|
||||||
|
|
||||||
await test.step('second export', async () => {
|
await test.step('second export', async () => {
|
||||||
await clickExportButton(page)
|
await clickExportButton(page, cmdBar)
|
||||||
|
|
||||||
await expect(exportingToastMessage).toBeVisible()
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
|
|
||||||
await clickExportButton(page)
|
await clickExportButton(page, cmdBar)
|
||||||
|
|
||||||
await test.step('The first export still succeeds', async () => {
|
await test.step('The first export still succeeds', async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -537,7 +535,7 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
|
|
||||||
await test.step('Successful, unblocked export', async () => {
|
await test.step('Successful, unblocked export', async () => {
|
||||||
// Try exporting again.
|
// Try exporting again.
|
||||||
await clickExportButton(page)
|
await clickExportButton(page, cmdBar)
|
||||||
|
|
||||||
// Find the toast.
|
// Find the toast.
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
@ -880,7 +878,7 @@ s2 = startSketchOn(XY)
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function clickExportButton(page: Page) {
|
async function clickExportButton(page: Page, cmdBar: CmdBarFixture) {
|
||||||
await test.step('Running export flow', async () => {
|
await test.step('Running export flow', async () => {
|
||||||
// export the model
|
// export the model
|
||||||
const exportButton = page.getByTestId('export-pane-button')
|
const exportButton = page.getByTestId('export-pane-button')
|
||||||
@ -896,9 +894,6 @@ async function clickExportButton(page: Page) {
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
const submitButton = page.getByText('Confirm Export')
|
await cmdBar.submit()
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ export const token = process.env.token || ''
|
|||||||
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
||||||
|
|
||||||
import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup'
|
import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup'
|
||||||
|
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||||
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
|
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
|
||||||
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
|
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
|
||||||
import { test } from '@e2e/playwright/zoo-test'
|
import { test } from '@e2e/playwright/zoo-test'
|
||||||
@ -737,6 +738,7 @@ export const doExport = async (
|
|||||||
output: Models['OutputFormat3d_type'],
|
output: Models['OutputFormat3d_type'],
|
||||||
rootDir: string,
|
rootDir: string,
|
||||||
page: Page,
|
page: Page,
|
||||||
|
cmdBar: CmdBarFixture,
|
||||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||||
): Promise<Paths> => {
|
): Promise<Paths> => {
|
||||||
if (exportFrom === 'dropdown') {
|
if (exportFrom === 'dropdown') {
|
||||||
@ -780,9 +782,7 @@ export const doExport = async (
|
|||||||
.click()
|
.click()
|
||||||
await page.locator('#arg-form').waitFor({ state: 'detached' })
|
await page.locator('#arg-form').waitFor({ state: 'detached' })
|
||||||
}
|
}
|
||||||
await expect(page.getByText('Confirm Export')).toBeVisible()
|
await cmdBar.submit()
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
|
||||||
|
|
||||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
test.describe('Testing constraints', () => {
|
test.describe('Testing constraints', () => {
|
||||||
test('Can constrain line length', async ({ page, homePage }) => {
|
test('Can constrain line length', async ({ page, homePage, cmdBar }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -50,11 +50,7 @@ test.describe('Testing constraints', () => {
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.getByTestId('constraint-length').click()
|
await page.getByTestId('constraint-length').click()
|
||||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
|
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
|
||||||
await page
|
await cmdBar.continue()
|
||||||
.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`length001 = 20sketch001 = startSketchOn(XY) |> startProfile(at = [-10, -10]) |> line(end = [20, 0]) |> angledLine(angle = 90, length = length001) |> xLine(length = -20)`
|
`length001 = 20sketch001 = startSketchOn(XY) |> startProfile(at = [-10, -10]) |> line(end = [20, 0]) |> angledLine(angle = 90, length = length001) |> xLine(length = -20)`
|
||||||
@ -681,9 +677,6 @@ test.describe('Testing constraints', () => {
|
|||||||
.getByRole('textbox')
|
.getByRole('textbox')
|
||||||
const cmdBarKclVariableNameInput =
|
const cmdBarKclVariableNameInput =
|
||||||
page.getByPlaceholder('Variable name')
|
page.getByPlaceholder('Variable name')
|
||||||
const cmdBarSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -736,7 +729,7 @@ part002 = startSketchOn(XZ)
|
|||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
const [ang, len] = value.split(', ')
|
const [ang, len] = value.split(', ')
|
||||||
const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
|
const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
|
||||||
await cmdBarSubmitButton.click()
|
await cmdBar.continue()
|
||||||
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
||||||
|
|
||||||
// checking active assures the cursor is where it should be
|
// checking active assures the cursor is where it should be
|
||||||
@ -1101,11 +1094,7 @@ part002 = startSketchOn(XZ)
|
|||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
|
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
|
||||||
await page
|
await cmdBar.continue()
|
||||||
.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
|
|
||||||
await pollEditorLinesSelectedLength(page, 1)
|
await pollEditorLinesSelectedLength(page, 1)
|
||||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
|
@ -21,7 +21,7 @@ test.describe('Testing loading external models', () => {
|
|||||||
// We have no more web tests
|
// We have no more web tests
|
||||||
test.fail(
|
test.fail(
|
||||||
'Web: should overwrite current code, cannot create new file',
|
'Web: should overwrite current code, cannot create new file',
|
||||||
async ({ editor, context, page, homePage }) => {
|
async ({ editor, context, page, homePage, cmdBar }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await test.step(`Test setup`, async () => {
|
await test.step(`Test setup`, async () => {
|
||||||
await context.addInitScript((code) => {
|
await context.addInitScript((code) => {
|
||||||
@ -52,9 +52,6 @@ test.describe('Testing loading external models', () => {
|
|||||||
name,
|
name,
|
||||||
})
|
})
|
||||||
const warningText = page.getByText('Overwrite current file with sample?')
|
const warningText = page.getByText('Overwrite current file with sample?')
|
||||||
const confirmButton = page.getByRole('button', {
|
|
||||||
name: 'Submit command',
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Precondition: check the initial code`, async () => {
|
await test.step(`Precondition: check the initial code`, async () => {
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
@ -70,7 +67,7 @@ test.describe('Testing loading external models', () => {
|
|||||||
await expect(commandMethodOption('Create new file')).not.toBeVisible()
|
await expect(commandMethodOption('Create new file')).not.toBeVisible()
|
||||||
await commandMethodOption('Overwrite').click()
|
await commandMethodOption('Overwrite').click()
|
||||||
await expect(warningText).toBeVisible()
|
await expect(warningText).toBeVisible()
|
||||||
await confirmButton.click()
|
await cmdBar.submit()
|
||||||
|
|
||||||
await editor.expectEditor.toContain('// ' + newSample.title)
|
await editor.expectEditor.toContain('// ' + newSample.title)
|
||||||
})
|
})
|
||||||
|
@ -3,6 +3,7 @@ import type { LineInputsType } from '@src/lang/std/sketchcombos'
|
|||||||
import { uuidv4 } from '@src/lib/utils'
|
import { uuidv4 } from '@src/lib/utils'
|
||||||
|
|
||||||
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
||||||
|
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||||
import { deg, getUtils, wiggleMove } from '@e2e/playwright/test-utils'
|
import { deg, getUtils, wiggleMove } from '@e2e/playwright/test-utils'
|
||||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
* @param {number} options.steps - The number of steps to perform
|
* @param {number} options.steps - The number of steps to perform
|
||||||
*/
|
*/
|
||||||
const _clickConstrained =
|
const _clickConstrained =
|
||||||
(page: Page, editor: EditorFixture) =>
|
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
|
||||||
async ({
|
async ({
|
||||||
hoverPos,
|
hoverPos,
|
||||||
constraintType,
|
constraintType,
|
||||||
@ -93,11 +94,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page
|
await cmdBar.continue()
|
||||||
.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
await editor.expectEditor.toContain(expectFinal, {
|
await editor.expectEditor.toContain(expectFinal, {
|
||||||
shouldNormalise: true,
|
shouldNormalise: true,
|
||||||
})
|
})
|
||||||
@ -113,7 +110,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
* @param {number} options.steps - The number of steps to perform
|
* @param {number} options.steps - The number of steps to perform
|
||||||
*/
|
*/
|
||||||
const _clickUnconstrained =
|
const _clickUnconstrained =
|
||||||
(page: Page, editor: EditorFixture) =>
|
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
|
||||||
async ({
|
async ({
|
||||||
hoverPos,
|
hoverPos,
|
||||||
constraintType,
|
constraintType,
|
||||||
@ -163,11 +160,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page
|
await cmdBar.continue()
|
||||||
.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
await editor.expectEditor.toContain(expectAfterUnconstrained, {
|
await editor.expectEditor.toContain(expectAfterUnconstrained, {
|
||||||
shouldNormalise: true,
|
shouldNormalise: true,
|
||||||
})
|
})
|
||||||
@ -239,8 +232,8 @@ test.describe('Testing segment overlays', () => {
|
|||||||
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(14)
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(14)
|
||||||
|
|
||||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
const clickUnconstrained = _clickUnconstrained(page, editor, cmdBar)
|
||||||
const clickConstrained = _clickConstrained(page, editor)
|
const clickConstrained = _clickConstrained(page, editor, cmdBar)
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.sendCustomCmd({
|
await u.sendCustomCmd({
|
||||||
@ -664,7 +657,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify the X constraint was added
|
// Verify the X constraint was added
|
||||||
await editor.expectEditor.toContain('center = [xAbs001, 0]', {
|
await editor.expectEditor.toContain('center = [xAbs001, 0]', {
|
||||||
@ -682,7 +675,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify the Y constraint was added
|
// Verify the Y constraint was added
|
||||||
await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', {
|
await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', {
|
||||||
@ -700,7 +693,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify all constraints were added
|
// Verify all constraints were added
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
@ -887,7 +880,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify the constraint was added
|
// Verify the constraint was added
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
@ -910,7 +903,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify both constraints were added
|
// Verify both constraints were added
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
@ -935,7 +928,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify the constraint was added
|
// Verify the constraint was added
|
||||||
await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', {
|
await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', {
|
||||||
@ -955,7 +948,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify all constraints were added
|
// Verify all constraints were added
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
|
@ -32,7 +32,7 @@ test('Units menu', async ({ page, homePage }) => {
|
|||||||
test(
|
test(
|
||||||
'Successful export shows a success toast',
|
'Successful export shows a success toast',
|
||||||
{ tag: '@skipLocalEngine' },
|
{ tag: '@skipLocalEngine' },
|
||||||
async ({ page, homePage, tronApp }) => {
|
async ({ page, homePage, cmdBar, tronApp }) => {
|
||||||
// FYI this test doesn't work with only engine running locally
|
// FYI this test doesn't work with only engine running locally
|
||||||
// And you will need to have the KittyCAD CLI installed
|
// And you will need to have the KittyCAD CLI installed
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
@ -94,7 +94,8 @@ part001 = startSketchOn(-XZ)
|
|||||||
presentation: 'pretty',
|
presentation: 'pretty',
|
||||||
},
|
},
|
||||||
tronApp?.projectDirName,
|
tronApp?.projectDirName,
|
||||||
page
|
page,
|
||||||
|
cmdBar
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -254,6 +255,7 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
|||||||
test('Basic default modeling and sketch hotkeys work', async ({
|
test('Basic default modeling and sketch hotkeys work', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await test.step(`Set up test`, async () => {
|
await test.step(`Set up test`, async () => {
|
||||||
@ -397,11 +399,8 @@ test('Basic default modeling and sketch hotkeys work', async ({
|
|||||||
await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({
|
await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({
|
||||||
timeout: 20_000,
|
timeout: 20_000,
|
||||||
})
|
})
|
||||||
await page.getByRole('button', { name: 'Continue' }).click()
|
await cmdBar.continue()
|
||||||
await expect(
|
await cmdBar.submit()
|
||||||
page.getByRole('button', { name: 'Submit command' })
|
|
||||||
).toBeVisible()
|
|
||||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
|
||||||
await expect(page.locator('.cm-content')).toContainText('extrude(')
|
await expect(page.locator('.cm-content')).toContainText('extrude(')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -575,8 +574,7 @@ profile001 = startProfile(sketch002, at = [-12.34, 12.34])
|
|||||||
|
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
await cmdBar.submit()
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
|
|
||||||
const result2 = result.genNext`
|
const result2 = result.genNext`
|
||||||
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`
|
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`
|
||||||
|
@ -21,7 +21,7 @@ export const CommandBar = () => {
|
|||||||
const commandBarState = useCommandBarState()
|
const commandBarState = useCommandBarState()
|
||||||
const { immediateState } = useNetworkContext()
|
const { immediateState } = useNetworkContext()
|
||||||
const {
|
const {
|
||||||
context: { selectedCommand, currentArgument, commands },
|
context: { selectedCommand, currentArgument, commands, argumentsToSubmit },
|
||||||
} = commandBarState
|
} = commandBarState
|
||||||
const isArgumentThatShouldBeHardToDismiss =
|
const isArgumentThatShouldBeHardToDismiss =
|
||||||
currentArgument?.inputType === 'selection' ||
|
currentArgument?.inputType === 'selection' ||
|
||||||
@ -68,16 +68,23 @@ export const CommandBar = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function stepBack() {
|
function stepBack() {
|
||||||
|
const entries = Object.entries(selectedCommand?.args || {}).filter(
|
||||||
|
([argName, arg]) => {
|
||||||
|
const argValue =
|
||||||
|
(typeof argumentsToSubmit[argName] === 'function'
|
||||||
|
? argumentsToSubmit[argName](commandBarState.context)
|
||||||
|
: argumentsToSubmit[argName]) || ''
|
||||||
|
const isRequired =
|
||||||
|
typeof arg.required === 'function'
|
||||||
|
? arg.required(commandBarState.context)
|
||||||
|
: arg.required
|
||||||
|
|
||||||
|
return !arg.hidden && (argValue || isRequired)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if (!currentArgument) {
|
if (!currentArgument) {
|
||||||
if (commandBarState.matches('Review')) {
|
if (commandBarState.matches('Review')) {
|
||||||
const entries = Object.entries(selectedCommand?.args || {}).filter(
|
|
||||||
([_, argConfig]) =>
|
|
||||||
!argConfig.hidden &&
|
|
||||||
(typeof argConfig.required === 'function'
|
|
||||||
? argConfig.required(commandBarState.context)
|
|
||||||
: argConfig.required)
|
|
||||||
)
|
|
||||||
|
|
||||||
const currentArgName = entries[entries.length - 1][0]
|
const currentArgName = entries[entries.length - 1][0]
|
||||||
const currentArg = {
|
const currentArg = {
|
||||||
name: currentArgName,
|
name: currentArgName,
|
||||||
@ -94,9 +101,6 @@ export const CommandBar = () => {
|
|||||||
commandBarActor.send({ type: 'Deselect command' })
|
commandBarActor.send({ type: 'Deselect command' })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const entries = Object.entries(selectedCommand?.args || {}).filter(
|
|
||||||
(a) => !a[1].hidden
|
|
||||||
)
|
|
||||||
const index = entries.findIndex(
|
const index = entries.findIndex(
|
||||||
([key, _]) => key === currentArgument.name
|
([key, _]) => key === currentArgument.name
|
||||||
)
|
)
|
||||||
@ -183,20 +187,6 @@ export const CommandBar = () => {
|
|||||||
<kbd className="hotkey ml-4 dark:!bg-chalkboard-80">esc</kbd>
|
<kbd className="hotkey ml-4 dark:!bg-chalkboard-80">esc</kbd>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</button>
|
</button>
|
||||||
{!commandBarState.matches('Selecting command') && (
|
|
||||||
<button onClick={stepBack} className="m-0 p-0 border-none">
|
|
||||||
<CustomIcon name="arrowLeft" className="w-5 h-5 rounded-sm" />
|
|
||||||
<Tooltip position="bottom">
|
|
||||||
Step back{' '}
|
|
||||||
<kbd className="hotkey ml-4 dark:!bg-chalkboard-80">
|
|
||||||
Shift
|
|
||||||
</kbd>
|
|
||||||
<kbd className="hotkey ml-4 dark:!bg-chalkboard-80">
|
|
||||||
Bksp
|
|
||||||
</kbd>
|
|
||||||
</Tooltip>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</WrapperComponent.Panel>
|
</WrapperComponent.Panel>
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import CommandArgOptionInput from '@src/components/CommandBar/CommandArgOptionInput'
|
import CommandArgOptionInput from '@src/components/CommandBar/CommandArgOptionInput'
|
||||||
import CommandBarBasicInput from '@src/components/CommandBar/CommandBarBasicInput'
|
import CommandBarBasicInput from '@src/components/CommandBar/CommandBarBasicInput'
|
||||||
import CommandBarHeader from '@src/components/CommandBar/CommandBarHeader'
|
import CommandBarHeaderFooter from '@src/components/CommandBar/CommandBarHeaderFooter'
|
||||||
import CommandBarKclInput from '@src/components/CommandBar/CommandBarKclInput'
|
import CommandBarKclInput from '@src/components/CommandBar/CommandBarKclInput'
|
||||||
import CommandBarPathInput from '@src/components/CommandBar/CommandBarPathInput'
|
import CommandBarPathInput from '@src/components/CommandBar/CommandBarPathInput'
|
||||||
import CommandBarSelectionInput from '@src/components/CommandBar/CommandBarSelectionInput'
|
import CommandBarSelectionInput from '@src/components/CommandBar/CommandBarSelectionInput'
|
||||||
import CommandBarSelectionMixedInput from '@src/components/CommandBar/CommandBarSelectionMixedInput'
|
import CommandBarSelectionMixedInput from '@src/components/CommandBar/CommandBarSelectionMixedInput'
|
||||||
import CommandBarTextareaInput from '@src/components/CommandBar/CommandBarTextareaInput'
|
import CommandBarTextareaInput from '@src/components/CommandBar/CommandBarTextareaInput'
|
||||||
|
import CommandBarDivider from '@src/components/CommandBar/CommandBarDivider'
|
||||||
import type { CommandArgument } from '@src/lib/commandTypes'
|
import type { CommandArgument } from '@src/lib/commandTypes'
|
||||||
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
|
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
|
||||||
|
|
||||||
@ -28,13 +29,14 @@ function CommandBarArgument({ stepBack }: { stepBack: () => void }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
currentArgument && (
|
currentArgument && (
|
||||||
<CommandBarHeader>
|
<CommandBarHeaderFooter stepBack={stepBack}>
|
||||||
<ArgumentInput
|
<ArgumentInput
|
||||||
arg={currentArgument}
|
arg={currentArgument}
|
||||||
stepBack={stepBack}
|
stepBack={stepBack}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
/>
|
/>
|
||||||
</CommandBarHeader>
|
<CommandBarDivider />
|
||||||
|
</CommandBarHeaderFooter>
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
5
src/components/CommandBar/CommandBarDivider.tsx
Normal file
5
src/components/CommandBar/CommandBarDivider.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default function CommandBarDivider() {
|
||||||
|
return (
|
||||||
|
<div className="block w-full my-2 h-[1px] bg-chalkboard-20 dark:bg-chalkboard-80" />
|
||||||
|
)
|
||||||
|
}
|
@ -5,6 +5,7 @@ import { useHotkeys } from 'react-hotkeys-hook'
|
|||||||
import { ActionButton } from '@src/components/ActionButton'
|
import { ActionButton } from '@src/components/ActionButton'
|
||||||
import { CustomIcon } from '@src/components/CustomIcon'
|
import { CustomIcon } from '@src/components/CustomIcon'
|
||||||
import Tooltip from '@src/components/Tooltip'
|
import Tooltip from '@src/components/Tooltip'
|
||||||
|
import CommandBarDivider from '@src/components/CommandBar/CommandBarDivider'
|
||||||
import type {
|
import type {
|
||||||
KclCommandValue,
|
KclCommandValue,
|
||||||
KclExpressionWithVariable,
|
KclExpressionWithVariable,
|
||||||
@ -14,7 +15,10 @@ import { getSelectionTypeDisplayText } from '@src/lib/selections'
|
|||||||
import { roundOff } from '@src/lib/utils'
|
import { roundOff } from '@src/lib/utils'
|
||||||
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
|
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
|
||||||
|
|
||||||
function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
|
function CommandBarHeaderFooter({
|
||||||
|
children,
|
||||||
|
stepBack,
|
||||||
|
}: React.PropsWithChildren<object> & { stepBack: () => void }) {
|
||||||
const commandBarState = useCommandBarState()
|
const commandBarState = useCommandBarState()
|
||||||
const {
|
const {
|
||||||
context: { selectedCommand, currentArgument, argumentsToSubmit },
|
context: { selectedCommand, currentArgument, argumentsToSubmit },
|
||||||
@ -102,19 +106,23 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
|
|||||||
<span className="pr-2" />
|
<span className="pr-2" />
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
{Object.entries(nonHiddenArgs || {})
|
{Object.entries(nonHiddenArgs || {}).flatMap(
|
||||||
.filter(
|
([argName, arg], i) => {
|
||||||
([_, argConfig]) =>
|
|
||||||
argConfig.skip === false ||
|
|
||||||
(typeof argConfig.required === 'function'
|
|
||||||
? argConfig.required(commandBarState.context)
|
|
||||||
: argConfig.required)
|
|
||||||
)
|
|
||||||
.map(([argName, arg], i) => {
|
|
||||||
const argValue =
|
const argValue =
|
||||||
(typeof argumentsToSubmit[argName] === 'function'
|
(typeof argumentsToSubmit[argName] === 'function'
|
||||||
? argumentsToSubmit[argName](commandBarState.context)
|
? argumentsToSubmit[argName](commandBarState.context)
|
||||||
: argumentsToSubmit[argName]) || ''
|
: argumentsToSubmit[argName]) || ''
|
||||||
|
const isCurrentArg = argName === currentArgument?.name
|
||||||
|
const isSkipFalse = arg.skip === false
|
||||||
|
const isRequired =
|
||||||
|
typeof arg.required === 'function'
|
||||||
|
? arg.required(commandBarState.context)
|
||||||
|
: arg.required
|
||||||
|
|
||||||
|
// We actually want to show non-hidden optional args that have a value set already
|
||||||
|
if (!(argValue || isCurrentArg || isSkipFalse || isRequired)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
@ -161,7 +169,9 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
|
|||||||
),
|
),
|
||||||
4
|
4
|
||||||
)
|
)
|
||||||
) : arg.inputType === 'text' && !arg.valueSummary ? (
|
) : arg.inputType === 'text' &&
|
||||||
|
!arg.valueSummary &&
|
||||||
|
typeof argValue === 'string' ? (
|
||||||
`${argValue.slice(0, 12)}${argValue.length > 12 ? '...' : ''}`
|
`${argValue.slice(0, 12)}${argValue.length > 12 ? '...' : ''}`
|
||||||
) : typeof argValue === 'object' ? (
|
) : typeof argValue === 'object' ? (
|
||||||
arg.valueSummary ? (
|
arg.valueSummary ? (
|
||||||
@ -207,8 +217,14 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
})}
|
}
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<CommandBarDivider />
|
||||||
|
{children}
|
||||||
|
<div className="px-4 pb-2 flex justify-between items-center gap-2">
|
||||||
|
<StepBackButton stepBack={stepBack} />
|
||||||
{isReviewing ? (
|
{isReviewing ? (
|
||||||
<ReviewingButton
|
<ReviewingButton
|
||||||
bgClassName={
|
bgClassName={
|
||||||
@ -237,8 +253,6 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="block w-full my-2 h-[1px] bg-chalkboard-20 dark:bg-chalkboard-80" />
|
|
||||||
{children}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -258,16 +272,16 @@ function ReviewingButton({ bgClassName, iconClassName }: ButtonProps) {
|
|||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
type="submit"
|
type="submit"
|
||||||
form="review-form"
|
form="review-form"
|
||||||
className="w-fit !p-0 rounded-sm hover:shadow focus:outline-current"
|
className={`w-fit !p-0 rounded-sm hover:brightness-110 hover:shadow focus:outline-current ${bgClassName}`}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
data-testid="command-bar-submit"
|
data-testid="command-bar-submit"
|
||||||
iconStart={{
|
iconEnd={{
|
||||||
icon: 'checkmark',
|
icon: 'checkmark',
|
||||||
bgClassName: `p-1 rounded-sm hover:brightness-110 ${bgClassName}`,
|
bgClassName: `p-1 rounded-sm ${bgClassName}`,
|
||||||
iconClassName: `${iconClassName}`,
|
iconClassName: `${iconClassName}`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="sr-only">Submit command</span>
|
<span className={`pl-2 ${iconClassName}`}>Submit</span>
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -278,18 +292,48 @@ function GatheringArgsButton({ bgClassName, iconClassName }: ButtonProps) {
|
|||||||
Element="button"
|
Element="button"
|
||||||
type="submit"
|
type="submit"
|
||||||
form="arg-form"
|
form="arg-form"
|
||||||
className="w-fit !p-0 rounded-sm hover:shadow focus:outline-current"
|
className={`w-fit !p-0 rounded-sm hover:brightness-110 hover:shadow focus:outline-current ${bgClassName}`}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
data-testid="command-bar-continue"
|
data-testid="command-bar-continue"
|
||||||
iconStart={{
|
iconEnd={{
|
||||||
icon: 'arrowRight',
|
icon: 'arrowRight',
|
||||||
bgClassName: `p-1 rounded-sm hover:brightness-110 ${bgClassName}`,
|
bgClassName: `p-1 rounded-sm ${bgClassName}`,
|
||||||
iconClassName: `${iconClassName}`,
|
iconClassName: `${iconClassName}`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="sr-only">Continue</span>
|
<span className={`pl-2 ${iconClassName}`}>Continue</span>
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CommandBarHeader
|
function StepBackButton({
|
||||||
|
bgClassName,
|
||||||
|
iconClassName,
|
||||||
|
stepBack,
|
||||||
|
}: ButtonProps & { stepBack: () => void }) {
|
||||||
|
return (
|
||||||
|
<ActionButton
|
||||||
|
Element="button"
|
||||||
|
type="button"
|
||||||
|
form="arg-form"
|
||||||
|
className={`w-fit !p-0 rounded-sm hover:brightness-110 hover:shadow focus:outline-current bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-chalkboard-20 dark:border-chalkboard-80 ${bgClassName}`}
|
||||||
|
tabIndex={0}
|
||||||
|
data-testid="command-bar-step-back"
|
||||||
|
iconStart={{
|
||||||
|
icon: 'arrowLeft',
|
||||||
|
bgClassName: `p-1 rounded-sm bg-chalkboard-20/50 dark:bg-chalkboard-80/50 ${bgClassName}`,
|
||||||
|
iconClassName: `${iconClassName}`,
|
||||||
|
}}
|
||||||
|
onClick={stepBack}
|
||||||
|
>
|
||||||
|
<span className={`pr-2 ${iconClassName}`}>Step back</span>
|
||||||
|
<Tooltip position="bottom">
|
||||||
|
Step back
|
||||||
|
<kbd className="hotkey ml-4 dark:!bg-chalkboard-80">Shift</kbd>
|
||||||
|
<kbd className="hotkey ml-2 dark:!bg-chalkboard-80">Bksp</kbd>
|
||||||
|
</Tooltip>
|
||||||
|
</ActionButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CommandBarHeaderFooter
|
@ -1,7 +1,10 @@
|
|||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
|
|
||||||
import CommandBarHeader from '@src/components/CommandBar/CommandBarHeader'
|
import CommandBarHeaderFooter from '@src/components/CommandBar/CommandBarHeaderFooter'
|
||||||
|
import CommandBarDivider from '@src/components/CommandBar/CommandBarDivider'
|
||||||
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
|
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { CustomIcon } from '@src/components/CustomIcon'
|
||||||
|
|
||||||
function CommandBarReview({ stepBack }: { stepBack: () => void }) {
|
function CommandBarReview({ stepBack }: { stepBack: () => void }) {
|
||||||
const commandBarState = useCommandBarState()
|
const commandBarState = useCommandBarState()
|
||||||
@ -57,19 +60,71 @@ function CommandBarReview({ stepBack }: { stepBack: () => void }) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const availableOptionalArgs = useMemo(() => {
|
||||||
|
if (!selectedCommand?.args) return undefined
|
||||||
|
const s = { ...selectedCommand.args }
|
||||||
|
for (const [name, arg] of Object.entries(s)) {
|
||||||
|
const value =
|
||||||
|
(typeof argumentsToSubmit[name] === 'function'
|
||||||
|
? argumentsToSubmit[name](commandBarState.context)
|
||||||
|
: argumentsToSubmit[name]) || ''
|
||||||
|
const isHidden =
|
||||||
|
typeof arg.hidden === 'function'
|
||||||
|
? arg.hidden(commandBarState.context)
|
||||||
|
: arg.hidden
|
||||||
|
const isRequired =
|
||||||
|
typeof arg.required === 'function'
|
||||||
|
? arg.required(commandBarState.context)
|
||||||
|
: arg.required
|
||||||
|
if (isHidden || isRequired || value) {
|
||||||
|
delete s[name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}, [selectedCommand, argumentsToSubmit, commandBarState.context])
|
||||||
return (
|
return (
|
||||||
<CommandBarHeader>
|
<CommandBarHeaderFooter stepBack={stepBack}>
|
||||||
<p className="px-4 pb-2">
|
{selectedCommand?.reviewMessage && (
|
||||||
{selectedCommand?.reviewMessage ? (
|
<>
|
||||||
selectedCommand.reviewMessage instanceof Function ? (
|
<p className="px-4 py-2">
|
||||||
selectedCommand.reviewMessage(commandBarState.context)
|
{selectedCommand.reviewMessage instanceof Function
|
||||||
) : (
|
? selectedCommand.reviewMessage(commandBarState.context)
|
||||||
selectedCommand.reviewMessage
|
: selectedCommand.reviewMessage}
|
||||||
)
|
</p>
|
||||||
) : (
|
<CommandBarDivider />
|
||||||
<>Confirm {selectedCommand?.displayName || selectedCommand?.name}</>
|
</>
|
||||||
)}
|
)}
|
||||||
</p>
|
{Object.entries(availableOptionalArgs || {}).length > 0 && (
|
||||||
|
<>
|
||||||
|
<div className="px-4 flex flex-wrap gap-2 items-baseline">
|
||||||
|
<span className="text-sm mr-4">Optional</span>
|
||||||
|
{Object.entries(availableOptionalArgs || {}).map(
|
||||||
|
([argName, arg]) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
data-testid="cmd-bar-add-optional-arg"
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
commandBarActor.send({
|
||||||
|
type: 'Edit argument',
|
||||||
|
data: { arg: { ...arg, name: argName } },
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
key={argName}
|
||||||
|
className="w-fit px-2 py-1 m-0 rounded-sm flex gap-2 items-center border"
|
||||||
|
>
|
||||||
|
<span className="capitalize">
|
||||||
|
{arg.displayName || argName}
|
||||||
|
</span>
|
||||||
|
<CustomIcon name="plus" className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<CommandBarDivider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<form
|
<form
|
||||||
id="review-form"
|
id="review-form"
|
||||||
className="absolute opacity-0 inset-0 pointer-events-none"
|
className="absolute opacity-0 inset-0 pointer-events-none"
|
||||||
@ -97,7 +152,7 @@ function CommandBarReview({ stepBack }: { stepBack: () => void }) {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</form>
|
</form>
|
||||||
</CommandBarHeader>
|
</CommandBarHeaderFooter>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,6 +275,10 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
['gltf', 'stl', 'ply'].includes(
|
['gltf', 'stl', 'ply'].includes(
|
||||||
commandContext.argumentsToSubmit.type as string
|
commandContext.argumentsToSubmit.type as string
|
||||||
),
|
),
|
||||||
|
hidden: (commandContext) =>
|
||||||
|
!['gltf', 'stl', 'ply'].includes(
|
||||||
|
commandContext.argumentsToSubmit.type as string
|
||||||
|
),
|
||||||
options: (commandContext) => {
|
options: (commandContext) => {
|
||||||
const type = commandContext.argumentsToSubmit.type as
|
const type = commandContext.argumentsToSubmit.type as
|
||||||
| OutputTypeKey
|
| OutputTypeKey
|
||||||
@ -428,9 +432,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
sectional: {
|
sectional: {
|
||||||
inputType: 'options',
|
inputType: 'options',
|
||||||
skip: true,
|
skip: true,
|
||||||
defaultValue: false,
|
required: false,
|
||||||
hidden: false,
|
|
||||||
required: true,
|
|
||||||
options: [
|
options: [
|
||||||
{ name: 'False', value: false },
|
{ name: 'False', value: false },
|
||||||
{ name: 'True', value: true },
|
{ name: 'True', value: true },
|
||||||
@ -464,6 +466,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
skip: true,
|
skip: true,
|
||||||
inputType: 'text',
|
inputType: 'text',
|
||||||
required: false,
|
required: false,
|
||||||
|
hidden: true,
|
||||||
},
|
},
|
||||||
sketches: {
|
sketches: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
@ -484,27 +487,27 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
axis: {
|
axis: {
|
||||||
required: (commandContext) =>
|
required: (context) =>
|
||||||
['Axis'].includes(
|
['Axis'].includes(context.argumentsToSubmit.axisOrEdge as string),
|
||||||
commandContext.argumentsToSubmit.axisOrEdge as string
|
|
||||||
),
|
|
||||||
inputType: 'options',
|
inputType: 'options',
|
||||||
displayName: 'Sketch Axis',
|
displayName: 'Sketch Axis',
|
||||||
options: [
|
options: [
|
||||||
{ name: 'X Axis', isCurrent: true, value: 'X' },
|
{ name: 'X Axis', isCurrent: true, value: 'X' },
|
||||||
{ name: 'Y Axis', isCurrent: false, value: 'Y' },
|
{ name: 'Y Axis', isCurrent: false, value: 'Y' },
|
||||||
],
|
],
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) =>
|
||||||
|
Boolean(context.argumentsToSubmit.nodeToEdit) ||
|
||||||
|
!['Axis'].includes(context.argumentsToSubmit.axisOrEdge as string),
|
||||||
},
|
},
|
||||||
edge: {
|
edge: {
|
||||||
required: (commandContext) =>
|
required: (context) =>
|
||||||
['Edge'].includes(
|
['Edge'].includes(context.argumentsToSubmit.axisOrEdge as string),
|
||||||
commandContext.argumentsToSubmit.axisOrEdge as string
|
|
||||||
),
|
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
|
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
|
||||||
multiple: false,
|
multiple: false,
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) =>
|
||||||
|
Boolean(context.argumentsToSubmit.nodeToEdit) ||
|
||||||
|
!['Edge'].includes(context.argumentsToSubmit.axisOrEdge as string),
|
||||||
},
|
},
|
||||||
angle: {
|
angle: {
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
@ -524,6 +527,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
skip: true,
|
skip: true,
|
||||||
inputType: 'text',
|
inputType: 'text',
|
||||||
required: false,
|
required: false,
|
||||||
|
hidden: true,
|
||||||
},
|
},
|
||||||
selection: {
|
selection: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
|
Reference in New Issue
Block a user