Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Tabs refactoring #1372

Merged
merged 2 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 37 additions & 47 deletions src/components/Tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@
<!-- --------------------------------------------------------------------------------------------------------------- -->

<template>
<div class="is-flex is-justify-content-space-between is-align-items-center mt-5 mb-4">
<div v-if="tabIds.length >= 1" class="is-flex is-justify-content-space-between is-align-items-center mt-5 mb-4">
<div class="tabs is-toggle h-is-property-text mb-1">
<ul>
<li v-for="(tab, i) in tabIds" :key="tab" :class="{'is-active':selection === tab}">
<a :id="'tab-' + tab" :style="{fontWeight: selection === tab ? 500 : 300}"
@click="handleSelect(tab)">
<li v-for="(tab, i) in tabIds" :key="tab" :class="{'is-active':selectedTab === tab}">
<a :id="'tab-' + tab" :style="{fontWeight: selectedTab === tab ? 500 : 300}"
@click="handleSelect(tab, true)">
<span>{{ tabLabels[i] ?? tab }}</span>
</a>
</li>
Expand All @@ -41,53 +41,43 @@
<!-- SCRIPT -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<script lang="ts">
<script setup lang="ts">

import {defineComponent, onMounted, PropType, ref} from "vue";
import {PropType, ref, watch} from "vue";

export default defineComponent({
name: "Tabs",

props: {
selectedTab: {
type: String,
required: true
},
tabIds: {
type: Array as PropType<string[]>,
required: true
},
tabLabels: {
type: Array as PropType<string[]>,
default: [] as string[] /* to please eslint */
}
const props = defineProps({
tabIds: {
type: Array as PropType<string[]>,
required: true
},

emits: ["update:selectedTab"],

setup(props, context) {

const selection = ref('')

onMounted(() => {
if (props.tabIds.includes(props.selectedTab)) {
selection.value = props.selectedTab
} else {
handleSelect(props.tabIds[0])
}
})

const handleSelect = (tab: string) => {
selection.value = tab
context.emit('update:selectedTab', tab)
}

return {
selection,
handleSelect,
}
tabLabels: {
type: Array as PropType<string[]>,
default: [] as string[] /* to please eslint */
}
});
})

const selectedTab = defineModel("selectedTab", {
type: String as PropType<string|null>,
default: null
})
const interactiveSelection = ref<boolean>(true) // true because initial value must be preserved

const handleSelect = (tab: string|null, interactive: boolean) => {
selectedTab.value = tab
interactiveSelection.value = interactive
}

const adjustSelectedTab = () => {
if (props.tabIds.length >= 1) {
if (selectedTab.value === null || props.tabIds.indexOf(selectedTab.value) == -1 || !interactiveSelection.value) {
handleSelect(props.tabIds[0], false)
} // else selectedTab remains unchanged
} else {
handleSelect(null, false)
}
}

watch(() => props.tabIds, adjustSelectedTab, {immediate: true})

</script>

Expand Down
4 changes: 2 additions & 2 deletions src/components/allowances/AllowancesSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ export default defineComponent({

const tabIds = ['hbar', 'token', 'nft']
const tabLabels = ['HBAR', 'Tokens', 'NFTs']
const selectedTab = ref(AppStorage.getAccountAllowanceTab() ?? tabIds[0])
const onUpdate = (tab: string) => {
const selectedTab = ref<string|null>(AppStorage.getAccountAllowanceTab() ?? tabIds[0])
const onUpdate = (tab: string|null) => {
selectedTab.value = tab
AppStorage.setAccountAllowanceTab(tab)
switch (selectedTab.value) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/contract/ContractByteCodeSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,8 @@ export default defineComponent({
const tabIds = ['abi', 'source', 'bytecode']
const tabLabels = ['ABI', 'Source', 'Bytecode']
const selectedOption = ref(AppStorage.getContractByteCodeTab() ?? tabIds[0])
const handleTabUpdate = (tab: string) => {
const selectedOption = ref<string|null>(AppStorage.getContractByteCodeTab() ?? tabIds[0])
const handleTabUpdate = (tab: string|null) => {
selectedOption.value = tab
AppStorage.setContractByteCodeTab(tab)
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/token/TokensSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ const accountId = computed(() => props.accountId)

const tabIds = ['fungible', 'nfts']
const tabLabels = ['Fungible', 'NFTs']
const selectedTab = ref(AppStorage.getAccountTokenTab() ?? tabIds[0])
const onSelectTab = (tab: string) => {
const selectedTab = ref<string|null>(AppStorage.getAccountTokenTab() ?? tabIds[0])
const onSelectTab = (tab: string|null) => {
selectedTab.value = tab
AppStorage.setAccountTokenTab(tab)
}
Expand Down
4 changes: 2 additions & 2 deletions src/pages/AccountDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -486,8 +486,8 @@ export default defineComponent({
const tabIds = ['transactions', 'contracts', 'rewards']
const tabLabels = ['Transactions', 'Created Contracts', 'Staking Rewards']
const selectedTab = ref(AppStorage.getAccountOperationTab() ?? tabIds[0])
const handleTabUpdate = (tab: string) => {
const selectedTab = ref<string|null>(AppStorage.getAccountOperationTab() ?? tabIds[0])
const handleTabUpdate = (tab: string|null) => {
selectedTab.value = tab
AppStorage.setAccountOperationTab(tab)
}
Expand Down
154 changes: 154 additions & 0 deletions tests/unit/Tabs.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// noinspection DuplicatedCode

/*-
*
* Hedera Mirror Node Explorer
Expand Down Expand Up @@ -171,5 +173,157 @@ describe("Tabs.vue", () => {
wrapper.unmount()
})

test("test with mutating IDs and labels", async () => {

await router.push("/") // To avoid "missing required param 'network'" error

//
// Stars with no tabs
//

const tabIds = []
const tabLabels = []

const wrapper = mount(Tabs, {
global: {
plugins: [router]
}, props: {
tabIds: tabIds,
tabLabels: tabLabels,
},
})

await flushPromises()

// console.log(wrapper.html())
// console.log(wrapper.text())

const tabs = wrapper.findAll('li')
expect(tabs.length).toBe(0)

// expect(tabs[selectedTab].attributes('class')).not.toContain('is-active')


//
// Add three tabs => tab1 is automatically selected
//
const tabIds2 = ['tab1', 'tab2', 'tab3']
const tabLabels2 = ['label1', 'label2', 'label3']
await wrapper.setProps({
tabIds: tabIds2,
tabLabels: tabLabels2,
})

// console.log(wrapper.html())
// console.log(wrapper.text())

const tabs2 = wrapper.findAll('li')
expect(tabs2.length).toBe(tabIds2.length)
expect(tabs2[0].text()).toContain("label1")

expect(tabs2[0].attributes('class')).toContain('is-active')

//
// Removes tab2 => tab1 remains selected
//
const tabIds3 = ['tab1', 'tab3']
const tabLabels3 = ['label1', 'label3']
await wrapper.setProps({
tabIds: tabIds3,
tabLabels: tabLabels3,
})

// console.log(wrapper.html())
// console.log(wrapper.text())

const tabs3 = wrapper.findAll('li')
expect(tabs3.length).toBe(tabIds3.length)

expect(tabs3[0].attributes('class')).toContain('is-active')
expect(tabs3[0].text()).toContain("label1")

//
// Removes tab1 => tab3 is selected
//

const tabIds4 = ['tab3']
const tabLabels4 = ['label3']
await wrapper.setProps({
tabIds: tabIds4,
tabLabels: tabLabels4,
})

// console.log(wrapper.html())
// console.log(wrapper.text())

const tabs4 = wrapper.findAll('li')
expect(tabs4.length).toBe(tabIds4.length)

expect(tabs4[0].attributes('class')).toContain('is-active')
expect(tabs4[0].text()).toContain("label3")



wrapper.unmount()
})

test("test with interactive", async () => {

await router.push("/") // To avoid "missing required param 'network'" error

//
// Starts with ['tab1', 'tab2', 'tab3'] => default selection is tab1
//

const tabIds = ['tab1', 'tab2', 'tab3']
const tabLabels = ['label1', 'label2', 'label3']

const wrapper = mount(Tabs, {
global: {
plugins: [router]
}, props: {
tabIds: tabIds,
tabLabels: tabLabels,
},
})

await flushPromises()

// console.log(wrapper.html())
// console.log(wrapper.text())

const tabs = wrapper.findAll('li')
expect(tabs.length).toBe(tabIds.length)

tabIds.forEach((id, index) => expect(wrapper.get('#tab-' + id).text()).toBe(tabLabels[index]))

expect(tabs[0].attributes('class')).toContain('is-active')

//
// Select tab3 interactively
//

const anchors = wrapper.findAll('a')
await anchors[2].trigger("click")
expect(tabs[2].attributes('class')).toContain('is-active')


//
// Change tab list to ['tab2', 'tab3'] => tab3 remains selected
//

const tabIds2 = ['tab2', 'tab3']
const tabLabels2 = ['label2', 'label3']
await wrapper.setProps({
tabIds: tabIds2,
tabLabels: tabLabels2,
})
const tabs2 = wrapper.findAll('li')
expect(tabs2.length).toBe(tabIds2.length)

expect(tabs2[1].attributes('class')).toContain('is-active')

wrapper.unmount()
})
})