Skip to content

Commit

Permalink
feat(console): monocao editor + tab bug fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Edward Irby committed Aug 30, 2023
1 parent 1cf63d6 commit 1c971ee
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 66 deletions.
8 changes: 7 additions & 1 deletion console/client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion console/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@
"@bufbuild/protobuf": "1.3.0",
"@headlessui/react": "1.7.16",
"@heroicons/react": "2.0.18",
"@monaco-editor/react": "^4.5.2",
"@monaco-editor/react": "4.5.2",
"@tailwindcss/forms": "^0.5.5",
"json-schema-faker": "0.5.0-rcv.46",
"json-schema": "0.4.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-router-dom": "6.15.0",
Expand Down
43 changes: 25 additions & 18 deletions console/client/src/features/verbs/VerbForm.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import {JSONSchemaFaker, Schema} from 'json-schema-faker'
import React, {useEffect} from 'react'
import {JSONSchemaFaker} from 'json-schema-faker'
import React from 'react'
import {CodeBlock} from '../../components/CodeBlock'
import {useClient} from '../../hooks/use-client'
import {Module, Verb} from '../../protos/xyz/block/ftl/v1/console/console_pb'
import {VerbService} from '../../protos/xyz/block/ftl/v1/ftl_connect'
import {VerbRef} from '../../protos/xyz/block/ftl/v1/schema/schema_pb'
import Editor from '@monaco-editor/react'
import Editor, {Monaco} from '@monaco-editor/react'
import {useDarkMode} from '../../providers/dark-mode-provider'
import type {JSONSchema4, JSONSchema6, JSONSchema7} from 'json-schema'

export type Schema = JSONSchema4 | JSONSchema6 | JSONSchema7

type Props = {
module?: Module
Expand All @@ -21,20 +24,22 @@ export const VerbForm: React.FC<Props> = ({module, verb}) => {
const [editorText, setEditorText] = React.useState<string>('')
const [response, setResponse] = React.useState<string | null>(null)
const [error, setError] = React.useState<string | null>(null)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const [schema, setSchema] = React.useState<Schema>()
useEffect(() => {
const [monaco, setMonaco] = React.useState<Monaco>()

React.useEffect(() => {
if (verb?.jsonRequestSchema) {
JSONSchemaFaker.option('maxItems', 2)
JSONSchemaFaker.option('alwaysFakeOptionals', true)

// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const verbSchema = JSON.parse(verb.jsonRequestSchema) as Schema
setSchema(verbSchema)
setEditorText(JSON.stringify(JSONSchemaFaker.generate(verbSchema), null, 2))
setEditorText(
JSON.stringify(JSONSchemaFaker.generate(verbSchema), null, 2)
)
}
}, [])

function handleEditorChange(value: string | undefined, _) {
setEditorText(value ?? '')
}
Expand Down Expand Up @@ -69,18 +74,20 @@ export const VerbForm: React.FC<Props> = ({module, verb}) => {
setError(String(error))
}
}
const handleEditorWillMount = monaco => {
verb?.jsonRequestSchema && monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: [{
uri: 'http://myserver/json-schema',
fileMatch: ['*'],
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
schema,
}],
})
const handleEditorWillMount = (monaco: Monaco) => {
setMonaco(monaco)
}

React.useEffect(() => {
schema &&
monaco?.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: [
{schema, uri: 'http://myserver/foo-schema.json', fileMatch: ['*']},
],
})
}, [monaco, schema])

return (
<>
<form
Expand Down
94 changes: 48 additions & 46 deletions console/client/src/layout/IDELayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,18 @@ import {
import {headerColor, headerTextColor, panelColor, invalidTab} from '../utils'
import {SidePanel} from './SidePanel'
import {Notification} from '../components/Notification'
import {useClient} from '../hooks/use-client'
import {ConsoleService} from '../protos/xyz/block/ftl/v1/console/console_connect'
import {modulesContext} from '../providers/modules-provider'
const selectedTabStyle = `${headerTextColor} ${headerColor}`
const unselectedTabStyle = `text-gray-300 bg-slate-100 dark:bg-slate-600`

export function IDELayout() {
const client = useClient(ConsoleService)
const {modules} = React.useContext(modulesContext)
const {tabs, activeTab, setActiveTab, setTabs} = React.useContext(TabsContext)
const [searchParams, setSearchParams] = useSearchParams()
const [activeIndex, setActiveIndex] = React.useState(0)
const [invalidTabMessage, setInvalidTabMessage] = React.useState<string>()
const id = searchParams.get(TabSearchParams.id) as string
const type = searchParams.get(TabSearchParams.type) as string
// Set active tab index whenever our activeTab context changes
React.useEffect(() => {
const index = tabs.findIndex(tab => tab.id === activeTab?.id)
setActiveIndex(index)
}, [activeTab])

const handleCloseTab = (id: string, index: number) => {
const nextActiveTab = {
Expand All @@ -59,52 +53,60 @@ export function IDELayout() {
})
}

// Handle loading a page with the tab query parameters
React.useEffect(() => {
const validateTabs = async () => {
const modules = await client.getModules({})
const msg = invalidTab({id, type})
if (msg) {
// IDs an invalid tab ID and type fallback to timeline
setActiveTab({id: timelineTab.id, type: timelineTab.type})
// On intial mount we have no query params set for tabs so we want to skip setting invalidTabMessage
if (type === null && id === null) return
return setInvalidTabMessage(msg)
const msg = invalidTab({id, type})
if (msg) {
// Default fallback to timeline
setActiveTab({id: timelineTab.id, type: timelineTab.type})
if (type === null && id === null) return

return setInvalidTabMessage(msg)
}
if (modules.length) {
const ids = id.split('.')
// Handle edge case where the id contains and invalid module or verb
if (modules.length) {
const [moduleId, verbId] = ids
// Check to see if they exist on controller
const moduleExist = modules.find(module => module?.name === moduleId)
const verbExist = moduleExist?.verbs.some(
({verb}) => verb?.name === verbId
)
if (moduleExist && !verbExist) {
setInvalidTabMessage(`Verb ${verbId} does not exist on ${moduleId}`)
return setActiveTab({id: timelineTab.id, type: timelineTab.type})
}
if (!moduleExist) {
setInvalidTabMessage(`Module ${moduleId} does not exist`)
return setActiveTab({id: timelineTab.id, type: timelineTab.type})
}
}
const inTabsList = tabs.some(
({id: tabId, type: tabType}) => tabId === id && tabType === type
)
// Tab is in tab list just set active tab
if (inTabsList) return setActiveTab({id, type})
// Get module and Verb ids
const [moduleId, verbId] = id.split('.')
// Check to see if they exist on controller
const moduleExist = modules.modules.find(
module => module?.name === moduleId
)
const verbExist = moduleExist?.verbs.some(
({verb}) => verb?.name === verbId
)
// Set tab if they both exists
if (moduleExist && verbExist) {
// Handle if tab is not already in tab list
if (
!tabs.some(
({id: tabId, type: tabType}) => tabId === id && tabType === type
)
) {
const newTab = {
id: moduleId,
label: verbId,
id,
label: ids[1],
type: TabType.Verb,
}
const nextTabs = [...tabs, newTab]
setActiveTab({id, type})
return setTabs(nextTabs)
setTabs(nextTabs)
return setActiveTab({id: newTab.id, type: newTab.type})
}
if (moduleExist && !verbExist) {
setInvalidTabMessage(`Verb ${verbId} does not exist on ${moduleId}`)
}
if (!moduleExist) {
setInvalidTabMessage(`Module ${moduleId} does not exist`)
}
setActiveTab({id: timelineTab.id, type: timelineTab.type})
// Handle if tab is in tab list
return setActiveTab({id, type})
}
void validateTabs()
}, [id, type])
}, [id, type, modules])

// Set active tab index whenever our activeTab context changes
React.useEffect(() => {
const index = tabs.findIndex(tab => tab.id === activeTab?.id)
setActiveIndex(index)
}, [activeTab])

return (
<>
Expand Down

0 comments on commit 1c971ee

Please sign in to comment.