Experiments (#1174)

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
This commit is contained in:
Liyas Thomas
2020-09-18 08:58:11 +05:30
committed by GitHub
parent 4ea60d3431
commit b7a44ab11f
7 changed files with 206 additions and 2 deletions

View File

@@ -394,6 +394,7 @@ input[type="radio"],
}
.method,
.url-field,
kbd,
select,
input,
@@ -409,7 +410,7 @@ code {
font-size: 16px;
font-family: "Roboto Mono", monospace;
font-weight: 400;
line-height: 1;
line-height: 1.25;
transition: all 0.2s ease-in-out;
user-select: text;
width: calc(100% - 8px);

View File

@@ -0,0 +1,35 @@
import urlField from "../url-field"
import { mount } from "@vue/test-utils"
const factory = (props) =>
mount(urlField, {
propsData: props,
})
/*
* NOTE : jsdom as of yet doesn't support contenteditable features
* hence, the test suite is pretty limited as it is not easy to test
* inputting values.
*/
describe("url-field", () => {
test("mounts properly", () => {
const wrapper = factory({
value: "test",
})
expect(wrapper.vm).toBeTruthy()
})
test("highlights environment variables", () => {
const wrapper = factory({
value: "https://hoppscotch.io/<<testa>>/<<testb>>",
})
const highlights = wrapper.findAll(".highlight-VAR").wrappers
expect(highlights).toHaveLength(2)
expect(highlights[0].text()).toEqual("<<testa>>")
expect(highlights[1].text()).toEqual("<<testb>>")
})
})

124
components/ui/url-field.vue Normal file
View File

@@ -0,0 +1,124 @@
<template>
<div contenteditable class="url-field" ref="editor" spellcheck="false"></div>
</template>
<style lang="scss">
.highlight-VAR {
color: var(--ac-color);
font-weight: bold;
}
</style>
<script>
export default {
props: {
value: { type: String },
},
mounted() {
this.$refs.editor.addEventListener("input", this.updateEditor)
this.$refs.editor.textContent = this.value || ""
this.updateEditor()
},
beforeDestroy() {
this.$refs.editor.removeEventListener("input", this.updateEditor)
},
methods: {
renderText(text) {
const fixedText = text.replace(/(\r\n|\n|\r)/gm, "").trim()
const parseMap = this.parseURL(fixedText)
const convertSpan = document.createElement("span")
const output = parseMap.map(([start, end, protocol]) => {
convertSpan.textContent = fixedText.substring(start, end + 1)
return `<span class='highlight-${protocol}'>${convertSpan.innerHTML}</span>`
})
return output.join("")
},
parseURL(text) {
const map = []
const regex = /<<\w+>>/
let match
let index = 0
while ((match = text.substring(index).match(regex))) {
map.push([index, index + (match.index - 1), "TEXT"])
map.push([index + match.index, index + match.index + match[0].length - 1, "VAR"])
index += match.index + match[0].length
if (index >= text.length - 1) break
}
if (text.length > index && !text.substring(index).match(regex)) {
map.push([index, text.length, "TEXT"])
}
return map
},
getTextSegments(element) {
const textSegments = []
Array.from(element.childNodes).forEach((node) => {
switch (node.nodeType) {
case Node.TEXT_NODE:
textSegments.push({ text: node.nodeValue, node })
break
case Node.ELEMENT_NODE:
textSegments.splice(textSegments.length, 0, ...this.getTextSegments(node))
break
}
})
return textSegments
},
restoreSelection(absoluteAnchorIndex, absoluteFocusIndex) {
const sel = window.getSelection()
const textSegments = this.getTextSegments(this.$refs.editor)
let anchorNode = this.$refs.editor
let anchorIndex = 0
let focusNode = this.$refs.editor
let focusIndex = 0
let currentIndex = 0
textSegments.forEach(({ text, node }) => {
const startIndexOfNode = currentIndex
const endIndexOfNode = startIndexOfNode + text.length
if (startIndexOfNode <= absoluteAnchorIndex && absoluteAnchorIndex <= endIndexOfNode) {
anchorNode = node
anchorIndex = absoluteAnchorIndex - startIndexOfNode
}
if (startIndexOfNode <= absoluteFocusIndex && absoluteFocusIndex <= endIndexOfNode) {
focusNode = node
focusIndex = absoluteFocusIndex - startIndexOfNode
}
currentIndex += text.length
})
sel.setBaseAndExtent(anchorNode, anchorIndex, focusNode, focusIndex)
},
updateEditor() {
const sel = window.getSelection()
const textSegments = this.getTextSegments(this.$refs.editor)
const textContent = textSegments.map(({ text }) => text).join("")
let anchorIndex = null
let focusIndex = null
let currentIndex = 0
textSegments.forEach(({ text, node }) => {
if (node === sel.anchorNode) {
anchorIndex = currentIndex + sel.anchorOffset
}
if (node === sel.focusNode) {
focusIndex = currentIndex + sel.focusOffset
}
currentIndex += text.length
})
this.$refs.editor.innerHTML = this.renderText(textContent)
this.restoreSelection(anchorIndex, focusIndex)
this.$emit("input", this.$refs.editor.textContent)
},
},
}
</script>

View File

@@ -302,5 +302,8 @@
"select_head_method": "Select HEAD method",
"select_post_method": "Select POST method",
"select_put_method": "Select PUT method",
"select_delete_method": "Select DELETE method"
"select_delete_method": "Select DELETE method",
"experiments": "Experiments",
"experiments_notice": "This is a collection of experiments we're working on that might turn out to be useful, fun, both, or neither. They're not final and may not be stable, so if something overly weird happens, don't panic. Just turn the dang thing off. Jokes aside, ",
"use_experimental_url_bar": "Use experimental URL bar with environment highlighting"
}

View File

@@ -145,6 +145,7 @@
<li>
<label for="url">{{ $t("url") }}</label>
<input
v-if="!this.$store.state.postwoman.settings.EXPERIMENTAL_URL_BAR_ENABLED"
:class="{ error: !isValidURL }"
@keyup.enter="isValidURL ? sendRequest() : null"
id="url"
@@ -154,6 +155,7 @@
spellcheck="false"
@input="pathInputHandler"
/>
<url-field v-model="uri" v-else />
</li>
<li class="shrink">
<label class="hide-on-small-screen" for="send">&nbsp;</label>

View File

@@ -209,6 +209,35 @@
</ul>
-->
</pw-section>
<pw-section class="red" :label="$t('experiments')" ref="experiments">
<ul class="info">
<li>
<p>
{{ $t("experiments_notice") }}
<a
class="link"
href="https://github.com/hoppscotch/hoppscotch/issues/new/choose"
target="_blank"
rel="noopener noreferrer"
>{{ $t("contact_us") }}</a
>.
</p>
</li>
</ul>
<ul>
<li>
<div class="flex-wrap">
<pw-toggle
:on="settings.EXPERIMENTAL_URL_BAR_ENABLED"
@change="toggleSetting('EXPERIMENTAL_URL_BAR_ENABLED')"
>
{{ $t("use_experimental_url_bar") }}
</pw-toggle>
</div>
</li>
</ul>
</pw-section>
</div>
</template>
@@ -325,6 +354,11 @@ export default {
typeof this.$store.state.postwoman.settings.EXTENSIONS_ENABLED !== "undefined"
? this.$store.state.postwoman.settings.EXTENSIONS_ENABLED
: true,
EXPERIMENTAL_URL_BAR_ENABLED:
typeof this.$store.state.postwoman.settings.EXPERIMENTAL_URL_BAR_ENABLED !== "undefined"
? this.$store.state.postwoman.settings.EXPERIMENTAL_URL_BAR_ENABLED
: false,
},
doneButton: '<i class="material-icons">done</i>',

View File

@@ -69,6 +69,11 @@ export const SETTINGS_KEYS = [
* to run the requests
*/
"EXTENSIONS_ENABLED",
/**
* A boolean value indicating whether to use the URL bar experiments
*/
"EXPERIMENTAL_URL_BAR_ENABLED",
]
export const state = () => ({