[Feat: GraphQL sidebar] GraphQL History (#1528)

* Create REQUIREMENTS.md

* graphql history UI

* rest history emit

* removed requirements file

* add, delete, clear, star history and sync with firstore

* use history

* empty schema

* remove other tabs

* computed query, setting headers

* binding props

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>

* remove print

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>

* remove dummy data

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>

* add docs tab

* date, time attribute --> updatedOn

* Removed margin from sidebar Tab

* removed v-bind

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>

* use shortcut for v-bind

* use shortcut for v-bind

* use unfold icons

* use template literals in :key

* history clear bug

* delete history bug

* minor translation

* remove console logs

* remove unused css

* add stared style

* remove absolute styles

* tests for graphql card

* tests for rest card

* tests for clear history added

* mount, clear, use, delete history tests added

* Rename card.vue to Card.vue

* Rename card.vue to Card.vue

* use computed

Co-authored-by: Isha Gupta <40794215+IshaGupta18@users.noreply.github.com>
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
This commit is contained in:
Osheen Sachdev
2021-03-11 08:42:09 +05:30
committed by GitHub
parent 51bd3455cc
commit 0b00842c50
10 changed files with 1019 additions and 229 deletions

View File

@@ -0,0 +1,109 @@
import GraphqlCard from "../graphql/Card"
import { mount } from "@vue/test-utils"
const factory = (props) => {
return mount(GraphqlCard, {
propsData: props,
stubs: {
"v-popover": {
template: "<div><slot /><slot name='popover' :is-open=true /></div>",
},
},
mocks: {
$t: (text) => text,
},
directives: {
tooltip() {
/* stub */
},
closePopover() {
/* stub */
},
},
})
}
const url = "https://dummydata.com"
const query = `query getUser($uid: String!) {
user(uid: $uid) {
name
}
}`
describe("GraphqlCard", () => {
test("Mounts properly if props are given", () => {
const wrapper = factory({
entry: {
type: "graphql",
url: url,
query: query,
star: false,
},
})
expect(wrapper).toBeTruthy()
})
test("toggle-star emitted on clicking on star button", async () => {
const wrapper = factory({
entry: {
type: "graphql",
url: url,
query: query,
star: true,
},
})
wrapper.find("button[data-testid='star_button']").trigger("click")
expect(wrapper.emitted("toggle-star")).toBeTruthy()
})
test("query expands on clicking the show more button", async () => {
const wrapper = factory({
entry: {
type: "graphql",
url: url,
query: query,
star: true,
},
})
expect(wrapper.vm.query).toStrictEqual([
`query getUser($uid: String!) {`,
` user(uid: $uid) {`,
`...`,
])
await wrapper.find("button[data-testid='query_expand']").trigger("click")
expect(wrapper.vm.query).toStrictEqual([
`query getUser($uid: String!) {`,
` user(uid: $uid) {`,
` name`,
` }`,
`}`,
])
})
test("use-entry emit on clicking the restore button", async () => {
const wrapper = factory({
entry: {
type: "graphql",
url: url,
query: query,
star: true,
},
})
await wrapper.find("button[data-testid='restore_history_entry']").trigger("click")
expect(wrapper.emitted("use-entry")).toBeTruthy()
})
test("delete-entry emit on clicking the delete button", async () => {
const wrapper = factory({
entry: {
type: "graphql",
url: url,
query: query,
star: true,
},
})
await wrapper.find("button[data-testid=delete_history_entry]").trigger("click")
expect(wrapper.emitted("delete-entry")).toBeTruthy()
})
})

View File

@@ -0,0 +1,285 @@
import History from "../"
import { fb } from "~/helpers/fb"
import { shallowMount } from "@vue/test-utils"
import HistoryRestCard from "../rest/Card"
const restHistory = [
{
id: "0",
type: "rest",
},
{
id: "1",
type: "rest",
},
{
id: "2",
type: "rest",
},
]
const graphqlHistory = [
{
id: "0",
type: "graphql",
},
{
id: "1",
type: "graphql",
},
{
id: "2",
type: "graphql",
},
]
var localStorageMock = (function () {
var store = {
history: JSON.stringify(restHistory),
graphqlHistory: JSON.stringify(graphqlHistory),
}
return {
getItem: function (key) {
return store[key]
},
setItem: jest.fn(),
clear: jest.fn(),
removeItem: jest.fn(),
}
})()
Object.defineProperty(window, "localStorage", { value: localStorageMock })
jest.mock("~/helpers/fb", () => ({
__esModule: true,
fb: {
currentUser: null,
currentHistory: restHistory,
currentGraphqlHistory: graphqlHistory,
clearHistory: jest.fn(),
clearGraphqlHistory: jest.fn(),
deleteHistory: jest.fn(),
deleteGraphqlHistory: jest.fn(),
},
}))
const factory = (props) => {
return shallowMount(History, {
propsData: props,
stubs: {
"v-popover": {
template: "<div><slot /><slot name='popover' :is-open=true /></div>",
},
HistoryRestCard: {
template: "<div data-testid='rest_card' />",
},
HistoryGraphqlCard: {
template: "<div data-testid='graphql_card' />",
},
AppSection: {
template: "<div><slot /></div>",
},
},
mocks: {
$t: (text) => text,
$toast: {
error() {},
},
},
directives: {
tooltip() {
/* stub */
},
closePopover() {
/* stub */
},
},
})
}
beforeEach(() => {
fb.clearHistory.mockClear()
fb.clearGraphqlHistory.mockClear()
fb.deleteHistory.mockClear()
fb.deleteGraphqlHistory.mockClear()
window.localStorage.setItem.mockClear()
})
describe("Mount History", () => {
test("Mounts rest history without login", async () => {
const wrapper = factory({
page: "rest",
})
expect(wrapper).toBeTruthy()
})
test("Mounts rest history with login", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "rest",
})
expect(wrapper).toBeTruthy()
})
test("Mounts graphql history without login", async () => {
const wrapper = factory({
page: "rest",
})
expect(wrapper).toBeTruthy()
})
test("Mounts graphql history with login", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "rest",
})
expect(wrapper).toBeTruthy()
})
})
describe("Clear History", () => {
test("Clear rest history without login", async () => {
fb.currentUser = null
const wrapper = factory({
page: "rest",
})
expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click")
expect(fb.clearHistory).not.toHaveBeenCalled()
expect(window.localStorage.setItem).toHaveBeenCalledWith("history", JSON.stringify([]))
})
test("Clear rest history with login", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "rest",
})
expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click")
expect(fb.clearHistory).toHaveBeenCalledTimes(1)
expect(window.localStorage.setItem).toHaveBeenCalledWith("history", JSON.stringify([]))
})
test("Dont confirm Clear rest history", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "rest",
})
expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='reject_clear_history']").trigger("click")
expect(fb.clearHistory).not.toHaveBeenCalled()
expect(window.localStorage.setItem).not.toHaveBeenCalledWith("history", JSON.stringify([]))
})
test("Clear graphql history without login", async () => {
fb.currentUser = null
const wrapper = factory({
page: "graphql",
})
expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click")
expect(fb.clearGraphqlHistory).not.toHaveBeenCalled()
expect(window.localStorage.setItem).toHaveBeenCalledWith("graphqlHistory", JSON.stringify([]))
})
test("Clear graphql history with login", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "graphql",
})
expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click")
expect(fb.clearGraphqlHistory).toHaveBeenCalledTimes(1)
expect(window.localStorage.setItem).toHaveBeenCalledWith("graphqlHistory", JSON.stringify([]))
})
test("Dont confirm Clear graphql history", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "graphql",
})
expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='reject_clear_history']").trigger("click")
expect(window.localStorage.setItem).not.toHaveBeenCalledWith(
"graphqlHistory",
JSON.stringify([])
)
})
})
describe("Use History", () => {
test("use rest history", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "rest",
})
expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(restHistory.length)
var index = restHistory.length - 1
wrapper.findAll("div[data-testid='rest_card']").at(index).vm.$emit("use-entry")
expect(wrapper.emitted("useHistory")).toBeTruthy()
expect(wrapper.emitted("useHistory")[0]).toStrictEqual([restHistory[index]])
})
test("use graphql history", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "graphql",
})
expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(graphqlHistory.length)
var index = restHistory.length - 1
wrapper.findAll("div[data-testid='graphql_card']").at(index).vm.$emit("use-entry")
expect(wrapper.emitted("useHistory")).toBeTruthy()
expect(wrapper.emitted("useHistory")[0]).toStrictEqual([graphqlHistory[index]])
})
})
describe("Delete History", () => {
test("delete rest history with login", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "rest",
})
expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(restHistory.length)
var index = 1
wrapper.findAll("div[data-testid='rest_card']").at(index).vm.$emit("delete-entry")
expect(fb.deleteHistory).toBeCalledWith(restHistory[index])
})
test("delete rest history without login", async () => {
fb.currentUser = null
const wrapper = factory({
page: "rest",
})
expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(restHistory.length)
var index = 1
wrapper.findAll("div[data-testid='rest_card']").at(index).vm.$emit("delete-entry")
expect(window.localStorage.setItem).toBeCalledWith(
"history",
JSON.stringify(restHistory.filter((entry) => entry.id != index))
)
})
test("delete graphql history with login", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "graphql",
})
expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(graphqlHistory.length)
var index = 1
wrapper.findAll("div[data-testid='graphql_card']").at(index).vm.$emit("delete-entry")
expect(fb.deleteGraphqlHistory).toBeCalledWith(graphqlHistory[index])
})
test("delete graphql history without login", async () => {
fb.currentUser = null
const wrapper = factory({
page: "graphql",
})
expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(graphqlHistory.length)
var index = 1
wrapper.findAll("div[data-testid='graphql_card']").at(index).vm.$emit("delete-entry")
expect(window.localStorage.setItem).toBeCalledWith(
"graphqlHistory",
JSON.stringify(graphqlHistory.filter((entry) => entry.id != index))
)
})
})

View File

@@ -0,0 +1,58 @@
import RestCard from "../rest/Card"
import { mount } from "@vue/test-utils"
const factory = (props) => {
return mount(RestCard, {
propsData: props,
stubs: {
"v-popover": {
template: "<div><slot /><slot name='popover' :is-open=true /></div>",
},
},
mocks: {
$t: (text) => text,
},
directives: {
tooltip() {
/* stub */
},
closePopover() {
/* stub */
},
},
})
}
const url = "https://dummydata.com/get"
const entry = {
type: "rest",
url: url,
method: "GET",
status: 200,
star: false,
}
describe("RestCard", () => {
test("Mounts properly if props are given", () => {
const wrapper = factory({ entry })
expect(wrapper).toBeTruthy()
})
test("toggle-star emitted on clicking on star button", async () => {
const wrapper = factory({ entry })
wrapper.find("button[data-testid='star_button']").trigger("click")
expect(wrapper.emitted("toggle-star")).toBeTruthy()
})
test("use-entry emit on clicking the restore button", async () => {
const wrapper = factory({ entry })
await wrapper.find("button[data-testid='restore_history_entry']").trigger("click")
expect(wrapper.emitted("use-entry")).toBeTruthy()
})
test("delete-entry emit on clicking the delete button", async () => {
const wrapper = factory({ entry })
await wrapper.find("button[data-testid=delete_history_entry]").trigger("click")
expect(wrapper.emitted("delete-entry")).toBeTruthy()
})
})

View File

@@ -0,0 +1,147 @@
<template>
<div>
<div class="show-on-large-screen">
<li>
<input
data-testid="'url'"
:aria-label="$t('url')"
type="text"
readonly
:value="entry.url"
:placeholder="$t('empty_req_name')"
class="bg-transparent"
/>
</li>
<button
data-testid="star_button"
class="icon"
:class="{ stared: entry.star }"
@click="$emit('toggle-star')"
v-tooltip="{
content: !entry.star ? $t('add_star') : $t('remove_star'),
}"
>
<i class="material-icons">
{{ entry.star ? "star" : "star_border" }}
</i>
</button>
<button
data-testid="query_expand"
class="icon"
@click="expand = !expand"
v-tooltip="{
content: expand ? $t('hide_more') : $t('show_more'),
}"
>
<i class="material-icons">
{{ expand ? "unfold_less" : "unfold_more" }}
</i>
</button>
<v-popover>
<button data-testid="options" class="tooltip-target icon" v-tooltip="$t('options')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button
data-testid="restore_history_entry"
class="icon"
@click="$emit('use-entry')"
:aria-label="$t('restore')"
v-close-popover
>
<i class="material-icons">restore</i>
<span>{{ $t("restore") }}</span>
</button>
</div>
<div>
<button
data-testid="delete_history_entry"
class="icon"
@click="$emit('delete-entry')"
:aria-label="$t('delete')"
v-close-popover
>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
<div class="show-on-large-screen">
<li data-testid="'query'">
<input
v-for="(line, index) in query"
:key="`line-${index}`"
:aria-label="$t('query')"
type="text"
readonly
:value="`${line}`"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
</div>
<transition name="fade">
<div v-if="showMore" class="show-on-large-screen">
<li>
<input
:aria-label="$t('time')"
type="text"
readonly
:value="entry.time"
v-tooltip="entry.date"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
<li>
<input
:aria-label="$t('duration')"
type="text"
readonly
:value="entry.duration"
:placeholder="$t('no_duration')"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
<li>
<input
:aria-label="$t('prerequest_script')"
type="text"
readonly
:value="entry.preRequestScript"
:placeholder="$t('no_prerequest_script')"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
</div>
</transition>
</div>
</template>
<style scoped lang="scss">
.stared {
color: #f8e81c !important;
}
</style>
<script>
export default {
props: {
entry: Object,
showMore: Boolean,
},
data() {
return {
expand: false,
}
},
computed: {
query() {
return this.expand
? this.entry.query.split("\n")
: this.entry.query.split("\n").slice(0, 2).concat(["..."])
},
},
}
</script>

View File

@@ -13,127 +13,24 @@
class="divide-y virtual-list divide-dashed divide-brdColor"
:class="{ filled: filteredHistory.length }"
>
<ul v-for="(entry, index) in filteredHistory" :key="index">
<div class="show-on-large-screen">
<span
class="p-2 m-2"
:class="findEntryStatus(entry).className"
:style="{ '--status-code': entry.status }"
>
{{ `${entry.method} \xA0 • \xA0 ${entry.status}` }}
</span>
<li>
<input
:aria-label="$t('token_req_name')"
type="text"
readonly
:value="entry.name"
:placeholder="$t('empty_req_name')"
class="bg-transparent"
/>
</li>
<button
class="icon"
:class="{ stared: entry.star }"
@click="toggleStar(entry)"
v-tooltip="{
content: !entry.star ? $t('add_star') : $t('remove_star'),
}"
>
<i class="material-icons">
{{ entry.star ? "star" : "star_border" }}
</i>
</button>
<!-- <li>
<button
class="icon"
v-tooltip="{
content: !entry.usesScripts
? 'No pre-request script'
: 'Used pre-request script'
}"
>
<i class="material-icons">
{{ !entry.usesScripts ? "http" : "code" }}
</i>
</button>
</li> -->
<v-popover>
<button class="tooltip-target icon" v-tooltip="$t('options')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button
class="icon"
@click="useHistory(entry)"
:aria-label="$t('edit')"
v-close-popover
>
<i class="material-icons">restore</i>
<span>{{ $t("restore") }}</span>
</button>
</div>
<div>
<button
class="icon"
@click="deleteHistory(entry)"
:aria-label="$t('delete')"
v-close-popover
>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
<div class="show-on-large-screen">
<li>
<input
:aria-label="$t('url')"
type="text"
readonly
:value="`${entry.url}${entry.path}`"
:placeholder="$t('no_url')"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
</div>
<transition name="fade">
<div v-if="showMore" class="show-on-large-screen">
<li>
<input
:aria-label="$t('time')"
type="text"
readonly
:value="entry.time"
v-tooltip="entry.date"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
<li>
<input
:aria-label="$t('duration')"
type="text"
readonly
:value="entry.duration"
:placeholder="$t('no_duration')"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
<li>
<input
:aria-label="$t('prerequest_script')"
type="text"
readonly
:value="entry.preRequestScript"
:placeholder="$t('no_prerequest_script')"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
</div>
</transition>
<ul v-for="(entry, index) in filteredHistory" :key="`entry-${index}`">
<HistoryRestCard
v-if="page == 'rest'"
:entry="entry"
:id="index"
:showMore="showMore"
@toggle-star="toggleStar(entry)"
@delete-entry="deleteHistory(entry)"
@use-entry="useHistory(entry)"
/>
<HistoryGraphqlCard
v-if="page == 'graphql'"
:entry="entry"
:showMore="showMore"
@toggle-star="toggleStar(entry)"
@delete-entry="deleteHistory(entry)"
@use-entry="useHistory(entry)"
/>
</ul>
</div>
<p :class="{ hidden: filteredHistory.length != 0 || history.length === 0 }" class="info">
@@ -144,12 +41,17 @@
</p>
<div v-if="history.length !== 0" class="rounded-b-lg bg-bgDarkColor">
<div class="row-wrapper" v-if="!isClearingHistory">
<button class="icon" :disabled="history.length === 0" @click="enableHistoryClearing">
<button
data-testid="clear_history"
class="icon"
:disabled="history.length === 0"
@click="enableHistoryClearing"
>
<i class="material-icons">clear_all</i>
<span>{{ $t("clear_all") }}</span>
</button>
<v-popover>
<button class="tooltip-target icon" v-tooltip="$t('sort')">
<button v-if="this.page == 'rest'" class="tooltip-target icon" v-tooltip="$t('sort')">
<i class="material-icons">sort</i>
</button>
<template slot="popover">
@@ -203,10 +105,20 @@
<div class="row-wrapper" v-else>
<p class="info"><i class="material-icons">help_outline</i> {{ $t("are_you_sure") }}</p>
<div>
<button class="icon" @click="clearHistory" v-tooltip="$t('yes')">
<button
data-testid="confirm_clear_history"
class="icon"
@click="clearHistory"
v-tooltip="$t('yes')"
>
<i class="material-icons">done</i>
</button>
<button class="icon" @click="disableHistoryClearing" v-tooltip="$t('no')">
<button
data-testid="reject_clear_history"
class="icon"
@click="disableHistoryClearing"
v-tooltip="$t('no')"
>
<i class="material-icons">close</i>
</button>
</div>
@@ -218,36 +130,26 @@
<style scoped lang="scss">
.virtual-list {
max-height: calc(100vh - 270px);
[readonly] {
cursor: default;
}
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.stared {
color: #f8e81c !important;
}
ul,
ol {
flex-direction: column;
}
@media (max-width: 720px) {
.virtual-list.filled {
min-height: 320px;
}
.labels {
display: none;
}
@@ -255,19 +157,23 @@ ol {
</style>
<script>
import findStatusGroup from "~/helpers/findStatusGroup"
import { fb } from "~/helpers/fb"
const updateOnLocalStorage = (propertyName, property) =>
window.localStorage.setItem(propertyName, JSON.stringify(property))
export default {
props: {
page: String,
},
data() {
return {
history:
fb.currentUser !== null
? fb.currentHistory
: JSON.parse(window.localStorage.getItem("history")) || [],
: JSON.parse(
window.localStorage.getItem(this.page == "rest" ? "history" : "graphqlHistory")
) || [],
filterText: "",
showFilter: false,
isClearingHistory: false,
@@ -283,8 +189,12 @@ export default {
filteredHistory() {
this.history =
fb.currentUser !== null
? fb.currentHistory
: JSON.parse(window.localStorage.getItem("history")) || []
? this.page == "rest"
? fb.currentHistory
: fb.currentGraphqlHistory
: JSON.parse(
window.localStorage.getItem(this.page == "rest" ? "history" : "graphqlHistory")
) || []
return this.history.filter((entry) => {
const filterText = this.filterText.toLowerCase()
return Object.keys(entry).some((key) => {
@@ -298,12 +208,12 @@ export default {
methods: {
async clearHistory() {
if (fb.currentUser !== null) {
await fb.clearHistory()
this.page == "rest" ? await fb.clearHistory() : await fb.clearGraphqlHistory()
}
this.history = []
this.filterText = ""
this.disableHistoryClearing()
updateOnLocalStorage("history", this.history)
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
this.$toast.error(this.$t("history_deleted"), {
icon: "delete",
})
@@ -311,30 +221,25 @@ export default {
useHistory(entry) {
this.$emit("useHistory", entry)
},
findEntryStatus({ status }) {
const foundStatusGroup = findStatusGroup(status)
return (
foundStatusGroup || {
className: "",
}
)
},
async deleteHistory(entry) {
if (fb.currentUser !== null) {
await fb.deleteHistory(entry)
}
this.history.splice(this.history.indexOf(entry), 1)
if (this.history.length === 0) {
this.filterText = ""
}
updateOnLocalStorage("history", this.history)
if (fb.currentUser !== null) {
await (this.page == "rest" ? fb.deleteHistory(entry) : fb.deleteGraphqlHistory(entry))
this.history = fb.currentHistory
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
} else {
this.history.splice(this.history.indexOf(entry), 1)
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
}
this.$toast.error(this.$t("deleted"), {
icon: "delete",
})
},
addEntry(entry) {
this.history.push(entry)
updateOnLocalStorage("history", this.history)
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
},
enableHistoryClearing() {
if (!this.history || !this.history.length) return
@@ -409,10 +314,12 @@ export default {
},
async toggleStar(entry) {
if (fb.currentUser !== null) {
await fb.toggleStar(entry, !entry.star)
this.page == "rest"
? await fb.toggleStar(entry, !entry.star)
: await fb.toggleGraphqlHistoryStar(entry, !entry.star)
}
entry.star = !entry.star
updateOnLocalStorage("history", this.history)
updateOnLocalStorage(this.page == "rest" ? "history" : "graphqlHistory", this.history)
},
},
}

View File

@@ -0,0 +1,160 @@
<template>
<div>
<div class="show-on-large-screen">
<span
class="p-2 m-2"
:class="entryStatus.className"
:style="{ '--status-code': entry.status }"
>
{{ `${entry.method} \xA0 • \xA0 ${entry.status}` }}
</span>
<li>
<input
:aria-label="$t('token_req_name')"
type="text"
readonly
:value="entry.name"
:placeholder="$t('empty_req_name')"
class="bg-transparent"
/>
</li>
<button
data-testid="star_button"
class="icon"
:class="{ stared: entry.star }"
@click="$emit('toggle-star')"
v-tooltip="{
content: !entry.star ? $t('add_star') : $t('remove_star'),
}"
>
<i class="material-icons">
{{ entry.star ? "star" : "star_border" }}
</i>
</button>
<!-- <li>
<button
class="icon"
v-tooltip="{
content: !entry.usesScripts
? 'No pre-request script'
: 'Used pre-request script'
}"
>
<i class="material-icons">
{{ !entry.usesScripts ? "http" : "code" }}
</i>
</button>
</li> -->
<v-popover>
<button class="tooltip-target icon" v-tooltip="$t('options')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button
data-testid="restore_history_entry"
class="icon"
@click="$emit('use-entry')"
:aria-label="$t('edit')"
v-close-popover
>
<i class="material-icons">restore</i>
<span>{{ $t("restore") }}</span>
</button>
</div>
<div>
<button
data-testid="delete_history_entry"
class="icon"
@click="$emit('delete-entry')"
:aria-label="$t('delete')"
v-close-popover
>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
<div class="show-on-large-screen">
<li>
<input
:aria-label="$t('url')"
type="text"
readonly
:value="`${entry.url}${entry.path}`"
:placeholder="$t('no_url')"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
</div>
<transition name="fade">
<div v-if="showMore" class="show-on-large-screen">
<li>
<input
:aria-label="$t('time')"
type="text"
readonly
:value="entry.time"
v-tooltip="entry.date"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
<li>
<input
:aria-label="$t('duration')"
type="text"
readonly
:value="entry.duration"
:placeholder="$t('no_duration')"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
<li>
<input
:aria-label="$t('prerequest_script')"
type="text"
readonly
:value="entry.preRequestScript"
:placeholder="$t('no_prerequest_script')"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/>
</li>
</div>
</transition>
</div>
</template>
<style scoped lang="scss">
.stared {
color: #f8e81c !important;
}
</style>
<script>
import findStatusGroup from "~/helpers/findStatusGroup"
export default {
props: {
entry: Object,
showMore: Boolean,
},
data() {
return {
expand: false,
}
},
computed: {
entryStatus() {
const foundStatusGroup = findStatusGroup(this.entry.status)
console.log(foundStatusGroup)
return (
foundStatusGroup || {
className: "",
}
)
},
},
}
</script>