Skip to content

Commit

Permalink
feat(@kong-ui-public/expressions): add router-playground-modal [KM-299]
Browse files Browse the repository at this point in the history
  • Loading branch information
2eha0 committed Jul 15, 2024
1 parent 99c9a48 commit f26aaff
Show file tree
Hide file tree
Showing 24 changed files with 1,798 additions and 19 deletions.
10 changes: 10 additions & 0 deletions packages/core/expressions/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from 'cypress'

export default defineConfig({
component: {
devServer: {
framework: 'vue',
bundler: 'vite',
},
},
})
5 changes: 5 additions & 0 deletions packages/core/expressions/cypress/fixtures/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
Empty file.
12 changes: 12 additions & 0 deletions packages/core/expressions/cypress/support/component-index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Components App</title>
</head>
<body>
<div data-cy-root></div>
</body>
</html>
32 changes: 32 additions & 0 deletions packages/core/expressions/cypress/support/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// ***********************************************************
// This example support/component.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')

import { mount } from 'cypress/vue'

// Augment the Cypress namespace to include type definitions for
// your custom command.
// Alternatively, can be defined in cypress/support/component.d.ts
// with a <reference path="./component" /> at the top of your spec.

Cypress.Commands.add('mount', mount)

// Example use:
// cy.mount(MyComponent)
27 changes: 27 additions & 0 deletions packages/core/expressions/cypress/support/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
declare namespace Cypress {
interface Chainable {
/**
* @description Custom alias command for cy.get() to select DOM element by data-testid attribute.
* @param {string} dataTestId
* @example cy.dataTestId('kong-auth-login-submit')
*/
getTestId(dataTestId: string): Chainable<Element>

/**
* @description Custom alias command for cy.find() to select DOM element by data-testid attribute.
* @param {string} dataTestId
* @example cy.findTestId('kong-auth-login-submit')
*/
findTestId(dataTestId: string): Chainable<Element>

/**
* @description Custom command to mount a Vue component inside Cypress browser.
* @example cy.mount(component, optionsOrProps)
* @param {any} component target component
* @param {any} options Options or props
*/
mount(component: any, options?: any): Chainable

assertValueCopiedToClipboard(value: string): Chainable
}
}
2 changes: 2 additions & 0 deletions packages/core/expressions/cypress/support/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Import custom Cypress commands
import './commands'
9 changes: 6 additions & 3 deletions packages/core/expressions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"stylelint": "stylelint --allow-empty-input './src/**/*.{css,scss,sass,less,styl,vue}'",
"stylelint:fix": "stylelint --allow-empty-input './src/**/*.{css,scss,sass,less,styl,vue}' --fix",
"typecheck": "vue-tsc -p './tsconfig.build.json' --noEmit",
"test:component": "BABEL_ENV=cypress cross-env FORCE_COLOR=1 cypress run --component -b chrome --spec './src/**/*.cy.ts' --project '../../../.'",
"test:component:open": "BABEL_ENV=cypress cross-env FORCE_COLOR=1 cypress open --component -b chrome --project '../../../.'",
"test:component": "BABEL_ENV=cypress cross-env FORCE_COLOR=1 cypress run --component -b chrome --spec './src/**/*.cy.ts' --project './'",
"test:component:open": "BABEL_ENV=cypress cross-env FORCE_COLOR=1 cypress open --component -b chrome --project './'",
"test:unit": "cross-env FORCE_COLOR=1 vitest run",
"test:unit:open": "cross-env FORCE_COLOR=1 vitest --ui"
},
Expand Down Expand Up @@ -70,6 +70,9 @@
"vue": "^3.4.31"
},
"dependencies": {
"@kong-ui-public/core": "workspace:^"
"@kong-ui-public/core": "workspace:^",
"@kong-ui-public/forms": "workspace:^",
"@kong/icons": "^1.14.2",
"uuid": "^9.0.1"
}
}
34 changes: 25 additions & 9 deletions packages/core/expressions/sandbox/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,24 @@
@parse-result-update="onParseResultUpdate"
/>

<a
href="#"
style="color: #0030cc; font-weight: bold;"
@click.prevent="isVisible = true"
>Test with Router Playground</a>

<RouterPlaygroundModal
:hide-editor-actions="false"
:initial-expression="expression"
:is-visible="isVisible"
@cancel="isVisible = false"
@commit="handleCommit"
>
<template #page-header>
<p>A playground where you can test out the Kong router Expressions.</p>
</template>
</RouterPlaygroundModal>

<div>
<p>ParseResult:</p>
<pre>{{ parseResult }}</pre>
Expand All @@ -41,6 +59,7 @@
import { ref, watch } from 'vue'
import type { SchemaDefinition } from '../src'
import { ExpressionsEditor, HTTP_SCHEMA_DEFINITION, STREAM_SCHEMA_DEFINITION } from '../src'
import RouterPlaygroundModal from '../src/components/RouterPlaygroundModal.vue'
type NamedSchemaDefinition = { name: string; definition: SchemaDefinition }
Expand Down Expand Up @@ -68,21 +87,18 @@ const btoa = (s: string) => window.btoa(s)
const expression = ref(expressionPresets[0])
const schemaDefinition = ref<NamedSchemaDefinition>(schemaPresets[0])
const parseResult = ref('')
const isVisible = ref(false)
const onParseResultUpdate = (result: any) => {
parseResult.value = JSON.stringify(result, null, 2)
}
const handleCommit = (exp: string) => {
expression.value = exp
isVisible.value = false
}
watch(schemaDefinition, (newSchemaDefinition) => {
schemaDefinition.value = newSchemaDefinition
})
</script>

<style lang="scss" scoped>
.presets {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
}
</style>
3 changes: 3 additions & 0 deletions packages/core/expressions/sandbox/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import '@kong/kongponents/dist/style.css'
import { createApp } from 'vue'
import App from './App.vue'
import Kongponents from '@kong/kongponents'

const app = createApp(App)

app.use(Kongponents)
app.mount('#app')
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<div
ref="root"
:class="editorClass"
:data-testid="testId"
/>
</template>

Expand All @@ -26,8 +27,10 @@ const props = withDefaults(defineProps<{
schema: Schema,
parseDebounce?: number,
inactiveUntilFocused?: boolean,
testId?: string,
}>(), {
parseDebounce: 500,
testId: 'expressions-editor',
})
const expression = defineModel<string>({ required: true })
Expand Down
103 changes: 103 additions & 0 deletions packages/core/expressions/src/components/MonacoEditor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<template>
<div ref="editorRoot" />
</template>

<script setup lang="ts">
import { ref, watch, onMounted, onBeforeUnmount, defineEmits, defineProps } from 'vue'
import * as monaco from 'monaco-editor'
const props = defineProps({
modelValue: {
type: String,
required: true,
},
theme: {
type: String,
default: 'vs',
},
language: {
type: String,
default: 'html',
},
options: {
type: Object,
default: () => ({}),
},
beforeInit: {
type: Function,
default: () => {},
},
validationResult: {
type: Object,
default: undefined,
},
})
const emits = defineEmits(['update:modelValue', 'editorWillMount', 'editorDidMount', 'validationResultUpdate'])
const editorRoot = ref<HTMLElement | null>(null)
let editor: monaco.editor.IStandaloneCodeEditor
watch(() => props.options, (newOptions) => {
if (editor) {
editor.updateOptions(newOptions)
}
}, { deep: true })
watch(() => props.modelValue, (newValue) => {
if (editor && newValue !== editor.getValue()) {
editor.setValue(newValue)
}
})
watch(() => props.language, (newVal) => {
if (editor) {
monaco.editor.setModelLanguage(editor.getModel()!, newVal)
}
})
watch(() => props.theme, (newVal) => {
if (editor) {
monaco.editor.setTheme(newVal)
}
})
watch(() => props.validationResult, (result) => {
emits('validationResultUpdate', result, editor)
})
onMounted(async () => {
if (typeof props.beforeInit === 'function') {
await props.beforeInit(monaco)
}
emits('editorWillMount', monaco)
const options = Object.assign(
{
value: props.modelValue,
theme: props.theme,
language: props.language,
},
props.options,
)
editor = monaco.editor.create(editorRoot.value as HTMLElement, options)
editor.onDidChangeModelContent((event) => {
const value = editor!.getValue()
if (props.modelValue !== value) {
emits('update:modelValue', value, event)
}
})
// emits('editorDidMount', editor)
})
onBeforeUnmount(() => {
if (editor) {
editor.dispose()
}
})
</script>
81 changes: 81 additions & 0 deletions packages/core/expressions/src/components/PageHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<template>
<header
class="page-header"
:data-testId="testId"
>
<div class="row">
<component
:is="tag"
:class="{ 'title-no-margin': noMargin }"
>
<span
v-if="!hideTitle"
class="title"
>{{ title }}</span>
<slot name="title-logo" />
</component>
<nav class="operations">
<slot />
</nav>
</div>
<slot name="below-title" />
</header>
</template>

<script setup lang="ts">
import { defineProps, computed } from 'vue'
const props = defineProps<{
title: string,
size: number,
hideTitle?: boolean,
noMargin?: boolean,
testId?: string,
}>()
const tag = computed(() => `h${props.size}`)
</script>

<style lang="scss" scoped>
.page-header h1, h2, h3, h4, h5, h6 {
color: $kui-color-text;
font-size: $kui-font-size-70;
font-weight: $kui-font-weight-bold;
.title {
word-break: break-all;
}
}
.row {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-left: -15px;
margin-right: -15px;
padding: 0 15px;
}
.col {
line-height: 38px;
}
h1, h2, h3, h4, h5, h6, nav {
margin-bottom: 20px;
margin-top: 0px;
}
.title-no-margin {
margin: 0 !important;
}
.operations {
align-items: center;
display: inline-flex;
flex-grow: 0;
justify-content: flex-end;
margin-left: auto;
text-align: right;
white-space: nowrap;
}
</style>
Loading

0 comments on commit f26aaff

Please sign in to comment.