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 16, 2024
1 parent 2ea7ad1 commit c16d749
Show file tree
Hide file tree
Showing 31 changed files with 2,004 additions and 20 deletions.
8 changes: 6 additions & 2 deletions packages/core/expressions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Reusable components to support [Kong's expressions language](https://docs.konghq
- `vue` must be initialized in the host application
- [`monaco-editor`](https://www.npmjs.com/package/monaco-editor) is required as a dependency in the host application
- [`vite-plugin-monaco-editor`](https://www.npmjs.com/package/vite-plugin-monaco-editor) is a required Vite plugin to bundle the Monaco Editor and its web workers
- [`@kong-ui-public/forms`](https://www.npmjs.com/package/@kong-ui-public/forms) is an optional dependency required for the `RouterPlaygroundModal` component

## Usage

Expand All @@ -27,6 +28,7 @@ Install required `dependencies` in your host application:

```sh
yarn add monaco-editor
yarn add @kong-ui-public/forms # optional: required for `RouterPlaygroundModal` component
```

Install required `devDependencies` in your host application:
Expand Down Expand Up @@ -58,9 +60,10 @@ Import the component(s) in your host application as well as the package styles:
```ts
import { asyncInit, ExpressionsEditor } from '@kong-ui-public/expressions'
import '@kong-ui-public/expressions/dist/style.css'
import '@kong-ui-public/forms/dist/style.css' // optional: required for `RouterPlaygroundModal` component
```
This package utilizes [vite-plugin-top-level-await](https://github.com/Menci/vite-plugin-top-level-await) to transform code in order to use top-level await on older browsers. To load the WASM correctly, you must use `await` or `Promise.then` to wait the imported `asyncInit` before using any other imported values.
This package utilizes [vite-plugin-top-level-await](https://github.com/Menci/vite-plugin-top-level-await) to transform code in order to use top-level await on older browsers. To load the WASM correctly, you must use `await` or `Promise.then` to wait the imported `asyncInit` before using any other imported values.
For example:
Expand All @@ -77,4 +80,5 @@ You can also make use of Vue's experimental [Suspense](https://vuejs.org/guide/b
## Individual component documentation
- [`<ExpressionsEditor.vue />`](docs/expressions-editor.md)
- [`<ExpressionsEditor />`](docs/expressions-editor.md)
- [`<RouterPlaygroundModal />`](docs/router-playground-modal.md)
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'
2 changes: 1 addition & 1 deletion packages/core/expressions/docs/expressions-editor.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ExpressionsEditor.vue
# ExpressionsEditor

A Monaco-based editor with autocomplete and syntax highlighting support for the expressions language.

Expand Down
80 changes: 80 additions & 0 deletions packages/core/expressions/docs/router-playground-modal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# RouterPlaygroundModal

A Monaco-based editor with autocomplete and syntax highlighting support for the expressions language.

- [Requirements](#requirements)
- [Usage](#usage)
- [Install](#install)
- [Props](#props)
- [Events](#events)
- [Usage example](#usage-example)
- [TypeScript definitions](#typescript-definitions)

## Requirements

[See requirements for the `@kong-ui-public/expressions` package.](../README.md#requirements)

## Usage

### Install

[See instructions for installing the `@kong-ui-public/expressions` package.](../README.md#install)

### Props

#### `isVisible`

- type: `boolean`
- required: `true`
- default: `false`

Controls whether the modal is visible or not.

#### `localstorageKey`

- type: `String`
- required: `optional`
- default: `kong-manager-router-playground-requests`

The key to use for storing the playground requests in the local storage.

#### `hideEditorActions`

- type: `boolean`
- required: `false`
- default: `false`

Controls whether the editor actions should be hidden or not.

#### `initialExpression`

- type: `string`
- required: `false`

The initial expression to be displayed in the editor.

### Events

#### change

An `change` event is emitted when the expression has been updated.

#### commit

An `commit` event is emitted when the expression has been committed.

#### cancel

A `cancel` event is emitted when the modal's cancel button has been clicked.

#### notify

A `notify` event is emitted when the component needs to notify some information to user.

### Usage example

Please refer to the [sandbox](../sandbox/App.vue).

## TypeScript definitions

TypeScript definitions are bundled with the package and can be directly imported into your host application.
11 changes: 8 additions & 3 deletions packages/core/expressions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@
"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"
},
"devDependencies": {
"@kong-ui-public/forms": "workspace:^",
"@kong/atc-router": "1.6.0-rc.1",
"@kong/design-tokens": "1.15.1",
"@kong/kongponents": "9.0.8",
Expand Down Expand Up @@ -64,12 +65,16 @@
"errorLimit": "2048KB"
},
"peerDependencies": {
"@kong-ui-public/forms": "workspace:^",
"@kong/atc-router": "^1.6.0-rc.1",
"@kong/kongponents": "^9.0.8",
"monaco-editor": "0.21.3",
"vue": "^3.4.31"
},
"dependencies": {
"@kong-ui-public/core": "workspace:^"
"@kong-ui-public/core": "workspace:^",
"@kong-ui-public/i18n": "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
Loading

0 comments on commit c16d749

Please sign in to comment.