TSK-1100 Fix saved views (#2942)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-04-11 19:39:23 +06:00 committed by GitHub
parent b8b8e8bd44
commit de11193e12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 229 additions and 115 deletions

View File

@ -1,18 +1,20 @@
<script lang="ts"> <script lang="ts">
import { DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core' import { DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
import { IntlString, translate } from '@hcengineering/platform' import { IntlString, translate } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation' import { createQuery } from '@hcengineering/presentation'
import { Issue } from '@hcengineering/tracker' import { Issue } from '@hcengineering/tracker'
import { Button, IconDetails, IconDetailsFilled } from '@hcengineering/ui' import { Button, IconDetails, IconDetailsFilled, location } from '@hcengineering/ui'
import view, { Viewlet } from '@hcengineering/view' import view, { Viewlet } from '@hcengineering/view'
import { import {
FilterBar, FilterBar,
getActiveViewletId,
getViewOptions,
setActiveViewletId,
ViewletSettingButton, ViewletSettingButton,
activeViewlet,
getViewOptions,
makeViewletKey,
setActiveViewletId,
viewOptionStore viewOptionStore
} from '@hcengineering/view-resources' } from '@hcengineering/view-resources'
import { onDestroy } from 'svelte'
import tracker from '../../plugin' import tracker from '../../plugin'
import IssuesContent from './IssuesContent.svelte' import IssuesContent from './IssuesContent.svelte'
import IssuesHeader from './IssuesHeader.svelte' import IssuesHeader from './IssuesHeader.svelte'
@ -33,26 +35,36 @@
$: if (query) updateSearchQuery(search) $: if (query) updateSearchQuery(search)
let resultQuery: DocumentQuery<Issue> = { ...searchQuery } let resultQuery: DocumentQuery<Issue> = { ...searchQuery }
const client = getClient()
let viewlets: WithLookup<Viewlet>[] = [] let viewlets: WithLookup<Viewlet>[] = []
$: update() $: update(viewlets, active)
const viewletQuery = createQuery()
async function update (): Promise<void> { viewletQuery.query(
viewlets = await client.findAll(
view.class.Viewlet, view.class.Viewlet,
{ attachTo: tracker.class.Issue, variant: { $ne: 'subissue' } }, { attachTo: tracker.class.Issue, variant: { $ne: 'subissue' } },
(res) => (viewlets = res),
{ {
lookup: { lookup: {
descriptor: view.class.ViewletDescriptor descriptor: view.class.ViewletDescriptor
} }
} }
) )
const _id = getActiveViewletId()
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0] let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
)
$: active = $activeViewlet[key]
async function update (viewlets: WithLookup<Viewlet>[], active: Ref<Viewlet> | null): Promise<void> {
viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id) setActiveViewletId(viewlet._id)
} }
$: if (!label && title) { $: if (!label && title) {
translate(title, {}).then((res) => { translate(title, {}).then((res) => {
label = res label = res

View File

@ -236,7 +236,7 @@
} }
</script> </script>
{#if categories.length === 0}} {#if categories.length === 0}
<Loading /> <Loading />
{:else} {:else}
<ActionContext <ActionContext

View File

@ -13,23 +13,25 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { DocumentQuery, WithLookup } from '@hcengineering/core' import { DocumentQuery, Ref, WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform' import { IntlString } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation' import { createQuery } from '@hcengineering/presentation'
import { Sprint } from '@hcengineering/tracker' import { Sprint } from '@hcengineering/tracker'
import { Button, IconAdd, Label, SearchEdit, showPopup } from '@hcengineering/ui' import { Button, IconAdd, Label, SearchEdit, location, showPopup } from '@hcengineering/ui'
import view, { Viewlet } from '@hcengineering/view' import view, { Viewlet } from '@hcengineering/view'
import { import {
FilterBar, FilterBar,
FilterButton, FilterButton,
getActiveViewletId, ViewletSettingButton,
activeViewlet,
getViewOptions, getViewOptions,
viewOptionStore, makeViewletKey,
setActiveViewletId, setActiveViewletId,
ViewletSettingButton viewOptionStore
} from '@hcengineering/view-resources' } from '@hcengineering/view-resources'
import { onDestroy } from 'svelte'
import tracker from '../../plugin' import tracker from '../../plugin'
import { getIncludedSprintStatuses, sprintTitleMap, SprintViewMode } from '../../utils' import { SprintViewMode, getIncludedSprintStatuses, sprintTitleMap } from '../../utils'
import NewSprint from './NewSprint.svelte' import NewSprint from './NewSprint.svelte'
import SprintContent from './SprintContent.svelte' import SprintContent from './SprintContent.svelte'
@ -57,25 +59,31 @@
$: title = sprintTitleMap[mode] $: title = sprintTitleMap[mode]
$: includedSprintsQuery = { status: { $in: includedSprintStatuses } } $: includedSprintsQuery = { status: { $in: includedSprintStatuses } }
const client = getClient()
let resultQuery: DocumentQuery<Sprint> = { ...searchQuery } let resultQuery: DocumentQuery<Sprint> = { ...searchQuery }
let viewlets: WithLookup<Viewlet>[] = [] let viewlets: WithLookup<Viewlet>[] = []
$: update() $: update(viewlets, active)
async function update (): Promise<void> { const viewletQuery = createQuery()
viewlets = await client.findAll( viewletQuery.query(view.class.Viewlet, { attachTo: tracker.class.Sprint }, (res) => (viewlets = res), {
view.class.Viewlet,
{ attachTo: tracker.class.Sprint },
{
lookup: { lookup: {
descriptor: view.class.ViewletDescriptor descriptor: view.class.ViewletDescriptor
} }
} })
let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
) )
const _id = getActiveViewletId()
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0] $: active = $activeViewlet[key]
async function update (viewlets: WithLookup<Viewlet>[], active: Ref<Viewlet> | null): Promise<void> {
viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id) setActiveViewletId(viewlet._id)
} }

View File

@ -1,18 +1,20 @@
<script lang="ts"> <script lang="ts">
import { DocumentQuery, WithLookup, Ref, Space } from '@hcengineering/core' import { DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
import { IntlString, translate } from '@hcengineering/platform' import { IntlString, translate } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation' import { createQuery } from '@hcengineering/presentation'
import { IssueTemplate } from '@hcengineering/tracker' import { IssueTemplate } from '@hcengineering/tracker'
import { Button, IconAdd, IconDetails, IconDetailsFilled, showPopup } from '@hcengineering/ui' import { Button, IconAdd, IconDetails, IconDetailsFilled, location, showPopup } from '@hcengineering/ui'
import view, { Viewlet } from '@hcengineering/view' import view, { Viewlet } from '@hcengineering/view'
import { import {
FilterBar, FilterBar,
getActiveViewletId, ViewletSettingButton,
activeViewlet,
getViewOptions, getViewOptions,
viewOptionStore, makeViewletKey,
setActiveViewletId setActiveViewletId,
viewOptionStore
} from '@hcengineering/view-resources' } from '@hcengineering/view-resources'
import { ViewletSettingButton } from '@hcengineering/view-resources' import { onDestroy } from 'svelte'
import tracker from '../../plugin' import tracker from '../../plugin'
import IssuesHeader from '../issues/IssuesHeader.svelte' import IssuesHeader from '../issues/IssuesHeader.svelte'
import CreateIssueTemplate from './CreateIssueTemplate.svelte' import CreateIssueTemplate from './CreateIssueTemplate.svelte'
@ -35,26 +37,32 @@
$: if (query) updateSearchQuery(search) $: if (query) updateSearchQuery(search)
let resultQuery: DocumentQuery<IssueTemplate> = { ...searchQuery } let resultQuery: DocumentQuery<IssueTemplate> = { ...searchQuery }
const client = getClient()
let viewlets: WithLookup<Viewlet>[] = [] let viewlets: WithLookup<Viewlet>[] = []
$: update() $: update(viewlets, active)
async function update (): Promise<void> { const viewletQuery = createQuery()
viewlets = await client.findAll( viewletQuery.query(view.class.Viewlet, { attachTo: tracker.class.IssueTemplate }, (res) => (viewlets = res), {
view.class.Viewlet,
{ attachTo: tracker.class.IssueTemplate },
{
lookup: { lookup: {
descriptor: view.class.ViewletDescriptor descriptor: view.class.ViewletDescriptor
} }
} })
let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
) )
const _id = getActiveViewletId()
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0] $: active = $activeViewlet[key]
async function update (viewlets: WithLookup<Viewlet>[], active: Ref<Viewlet> | null): Promise<void> {
viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id) setActiveViewletId(viewlet._id)
} }
$: if (!label && title) { $: if (!label && title) {
translate(title, {}).then((res) => { translate(title, {}).then((res) => {
label = res label = res

View File

@ -50,7 +50,7 @@ import {
} from '@hcengineering/ui' } from '@hcengineering/ui'
import type { BuildModelOptions, Viewlet, ViewletDescriptor } from '@hcengineering/view' import type { BuildModelOptions, Viewlet, ViewletDescriptor } from '@hcengineering/view'
import view, { AttributeModel, BuildModelKey } from '@hcengineering/view' import view, { AttributeModel, BuildModelKey } from '@hcengineering/view'
import { writable } from 'svelte/store' import { get, writable } from 'svelte/store'
import plugin from './plugin' import plugin from './plugin'
import { noCategory } from './viewOptions' import { noCategory } from './viewOptions'
@ -474,20 +474,27 @@ export function categorizeFields (
return result return result
} }
function makeViewletKey (loc?: Location): string { export function makeViewletKey (loc?: Location): string {
loc = loc ?? getCurrentLocation() loc = loc ?? getCurrentLocation()
loc.fragment = undefined loc.fragment = undefined
loc.query = undefined loc.query = undefined
return 'viewlet' + locationToUrl(loc) return 'viewlet' + locationToUrl(loc)
} }
export const activeViewlet = writable<Record<string, Ref<Viewlet> | null>>({})
export function setActiveViewletId (viewletId: Ref<Viewlet> | null, loc?: Location): void { export function setActiveViewletId (viewletId: Ref<Viewlet> | null, loc?: Location): void {
const key = makeViewletKey(loc) const key = makeViewletKey(loc)
const current = get(activeViewlet) ?? {}
if (viewletId !== null && viewletId !== undefined) { if (viewletId !== null && viewletId !== undefined) {
localStorage.setItem(key, viewletId) localStorage.setItem(key, viewletId)
current[key] = viewletId
} else { } else {
localStorage.removeItem(key) localStorage.removeItem(key)
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete current[key]
} }
activeViewlet.set(current)
} }
export function getActiveViewletId (): Ref<Viewlet> | null { export function getActiveViewletId (): Ref<Viewlet> | null {

View File

@ -40,8 +40,8 @@ export function isDropdownType (viewOption: ViewOptionModel): viewOption is Drop
return viewOption.type === 'dropdown' return viewOption.type === 'dropdown'
} }
function makeViewOptionsKey (viewlet: Viewlet): string { export function makeViewOptionsKey (viewlet: Ref<Viewlet>, variant?: string): string {
const prefix = viewlet?._id + (viewlet?.variant !== undefined ? `-${viewlet.variant}` : '') const prefix = viewlet + (variant !== undefined ? `-${variant}` : '')
const loc = getCurrentLocation() const loc = getCurrentLocation()
loc.fragment = undefined loc.fragment = undefined
loc.query = undefined loc.query = undefined
@ -49,7 +49,7 @@ function makeViewOptionsKey (viewlet: Viewlet): string {
} }
export function setViewOptions (viewlet: Viewlet, options: ViewOptions): void { export function setViewOptions (viewlet: Viewlet, options: ViewOptions): void {
const key = makeViewOptionsKey(viewlet) const key = makeViewOptionsKey(viewlet._id, viewlet.variant)
localStorage.setItem(key, JSON.stringify(options)) localStorage.setItem(key, JSON.stringify(options))
setStore(key, options) setStore(key, options)
} }
@ -61,7 +61,7 @@ function setStore (key: string, options: ViewOptions): void {
} }
function _getViewOptions (viewlet: Viewlet, viewOptionStore: Map<string, ViewOptions>): ViewOptions | null { function _getViewOptions (viewlet: Viewlet, viewOptionStore: Map<string, ViewOptions>): ViewOptions | null {
const key = makeViewOptionsKey(viewlet) const key = makeViewOptionsKey(viewlet._id, viewlet.variant)
const store = viewOptionStore.get(key) const store = viewOptionStore.get(key)
if (store !== undefined) { if (store !== undefined) {
return store return store

View File

@ -2,15 +2,19 @@
import { Doc, Ref } from '@hcengineering/core' import { Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import setting from '@hcengineering/setting' import setting from '@hcengineering/setting'
import { Action, navigate, getCurrentLocation, location } from '@hcengineering/ui' import { Action, Location, location, navigate } from '@hcengineering/ui'
import view, { FilteredView } from '@hcengineering/view' import view, { Filter, FilteredView } from '@hcengineering/view'
import { import {
TreeItem,
TreeNode,
activeViewlet,
filterStore, filterStore,
getFilterKey, getFilterKey,
makeViewOptionsKey,
makeViewletKey,
setActiveViewletId, setActiveViewletId,
setViewOptions, setViewOptions,
TreeItem, viewOptionStore
TreeNode
} from '@hcengineering/view-resources' } from '@hcengineering/view-resources'
import { Application } from '@hcengineering/workbench' import { Application } from '@hcengineering/workbench'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
@ -61,24 +65,35 @@
$filterStore = JSON.parse(fv.filters) $filterStore = JSON.parse(fv.filters)
} }
let oldLocation: string = ''
$: loc = $location
const clearSelection = () => { const clearSelection = () => {
selectedId = selectedFW = undefined selectedId = selectedFW = undefined
oldLocation = getCurrentLocation().path.join()
dispatch('select', false) dispatch('select', false)
} }
$: fs = $filterStore function checkSelected (fs: Filter[], loc: Location, filteredViews: FilteredView[] | undefined) {
$: if (loc && Array.isArray(fs) && fs.length > 0 && Array.isArray(filteredViews)) {
const filters = JSON.stringify(fs) const filters = JSON.stringify(fs)
selectedFW = filteredViews.filter((fv) => fv.filters === filters) if (loc && Array.isArray(fs) && fs.length > 0 && Array.isArray(filteredViews)) {
if (selectedFW.length > 0 && selectedFW[0].location.path.join() === loc.path.join()) { for (const fv of filteredViews) {
selectedId = selectedFW[0]._id if (fv.location.path.join() !== loc.path.join()) continue
oldLocation = selectedFW[0].location.path.join() if (fv.filters !== filters) continue
const key = makeViewletKey(loc)
if (fv.viewletId !== $activeViewlet[key]) continue
if (fv.viewletId !== null) {
const optionKey = makeViewOptionsKey(fv.viewletId)
const viewOptions = $viewOptionStore.get(optionKey)
if (JSON.stringify(fv.viewOptions) !== JSON.stringify(viewOptions)) continue
}
selectedId = fv._id
dispatch('select', true) dispatch('select', true)
} else clearSelection() return
} else clearSelection() }
clearSelection()
} else {
clearSelection()
}
}
$: checkSelected($filterStore, $location, filteredViews)
$: dispatch('shown', filteredViews !== undefined && filteredViews.length > 0) $: dispatch('shown', filteredViews !== undefined && filteredViews.length > 0)
</script> </script>

View File

@ -20,15 +20,21 @@
import { import {
AnyComponent, AnyComponent,
Button, Button,
deviceOptionsStore as deviceInfo,
IconAdd, IconAdd,
SearchEdit, SearchEdit,
showPopup, TabList,
TabList deviceOptionsStore as deviceInfo,
location,
showPopup
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { Viewlet, ViewOptions } from '@hcengineering/view' import { ViewOptions, Viewlet } from '@hcengineering/view'
import { getActiveViewletId, setActiveViewletId, ViewletSettingButton } from '@hcengineering/view-resources' import {
import { createEventDispatcher } from 'svelte' ViewletSettingButton,
activeViewlet,
makeViewletKey,
setActiveViewletId
} from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy } from 'svelte'
import Header from './Header.svelte' import Header from './Header.svelte'
export let spaceId: Ref<Space> | undefined export let spaceId: Ref<Space> | undefined
@ -54,19 +60,28 @@
showPopup(createItemDialog as AnyComponent, { space: spaceId }, 'top') showPopup(createItemDialog as AnyComponent, { space: spaceId }, 'top')
} }
$: updateViewlets(viewlets) $: update(viewlets, active)
$: if (prevSpaceId !== spaceId) { $: if (prevSpaceId !== spaceId) {
search = '' search = ''
dispatch('search', '') dispatch('search', '')
} }
function updateViewlets (viewlets: WithLookup<Viewlet>[]) { let key = makeViewletKey()
const _id = getActiveViewletId()
const index = viewlets.findIndex((p) => p._id === (viewlet?._id ?? _id)) onDestroy(
viewlet = index === -1 ? viewlets[0] : viewlets[index] location.subscribe((loc) => {
key = makeViewletKey(loc)
})
)
$: active = $activeViewlet[key]
async function update (viewlets: WithLookup<Viewlet>[], active: Ref<Viewlet> | null): Promise<void> {
viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id) setActiveViewletId(viewlet._id)
} }
$: viewslist = viewlets.map((views) => { $: viewslist = viewlets.map((views) => {
return { return {
id: views._id, id: views._id,

View File

@ -16,10 +16,17 @@
import core, { Class, Doc, Ref, Space, WithLookup } from '@hcengineering/core' import core, { Class, Doc, Ref, Space, WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform' import { IntlString } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation' import { getClient } from '@hcengineering/presentation'
import { AnyComponent, Component } from '@hcengineering/ui' import { AnyComponent, Component, location } from '@hcengineering/ui'
import view, { Viewlet } from '@hcengineering/view' import view, { Viewlet } from '@hcengineering/view'
import { getActiveViewletId, getViewOptions, viewOptionStore } from '@hcengineering/view-resources' import {
activeViewlet,
getViewOptions,
makeViewletKey,
setActiveViewletId,
viewOptionStore
} from '@hcengineering/view-resources'
import type { ViewConfiguration } from '@hcengineering/workbench' import type { ViewConfiguration } from '@hcengineering/workbench'
import { onDestroy } from 'svelte'
import SpaceContent from './SpaceContent.svelte' import SpaceContent from './SpaceContent.svelte'
import SpaceHeader from './SpaceHeader.svelte' import SpaceHeader from './SpaceHeader.svelte'
@ -38,9 +45,22 @@
let viewlets: WithLookup<Viewlet>[] = [] let viewlets: WithLookup<Viewlet>[] = []
$: update(currentSpace, currentView?.class) let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
)
async function update (currentSpace?: Ref<Space>, attachTo?: Ref<Class<Doc>>): Promise<void> { $: active = $activeViewlet[key]
$: update(active, currentSpace, currentView?.class)
async function update (
active: Ref<Viewlet> | null,
currentSpace?: Ref<Space>,
attachTo?: Ref<Class<Doc>>
): Promise<void> {
if (currentSpace === undefined) { if (currentSpace === undefined) {
space = undefined space = undefined
return return
@ -62,8 +82,8 @@
} }
) )
if (header !== undefined) { if (header !== undefined) {
const _id = getActiveViewletId() viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0] setActiveViewletId(viewlet._id)
} }
_class = attachTo _class = attachTo
} }

View File

@ -20,25 +20,28 @@
AnyComponent, AnyComponent,
Button, Button,
Component, Component,
deviceOptionsStore as deviceInfo,
Icon, Icon,
IconAdd, IconAdd,
Label, Label,
Loading, Loading,
SearchEdit, SearchEdit,
showPopup, TabList,
TabList deviceOptionsStore as deviceInfo,
location,
showPopup
} from '@hcengineering/ui' } from '@hcengineering/ui'
import view, { Viewlet, ViewletDescriptor, ViewletPreference } from '@hcengineering/view' import view, { Viewlet, ViewletDescriptor, ViewletPreference } from '@hcengineering/view'
import { import {
FilterBar, FilterBar,
FilterButton, FilterButton,
getActiveViewletId,
getViewOptions,
setActiveViewletId,
ViewletSettingButton, ViewletSettingButton,
activeViewlet,
getViewOptions,
makeViewletKey,
setActiveViewletId,
viewOptionStore viewOptionStore
} from '@hcengineering/view-resources' } from '@hcengineering/view-resources'
import { onDestroy } from 'svelte'
export let _class: Ref<Class<Doc>> export let _class: Ref<Class<Doc>>
export let space: Ref<Space> | undefined = undefined export let space: Ref<Space> | undefined = undefined
@ -67,9 +70,35 @@
let viewlets: WithLookup<Viewlet>[] = [] let viewlets: WithLookup<Viewlet>[] = []
$: update(_class, descriptors) const viewletQuery = createQuery()
$: viewletQuery.query(
view.class.Viewlet,
{ attachTo: _class, variant: { $exists: false } },
(res) => (viewlets = res),
{
lookup: {
descriptor: view.class.ViewletDescriptor
}
}
)
async function update (_class: Ref<Class<Doc>>, descriptors?: Ref<ViewletDescriptor>[]): Promise<void> { let key = makeViewletKey()
onDestroy(
location.subscribe((loc) => {
key = makeViewletKey(loc)
})
)
$: active = $activeViewlet[key]
$: update(_class, active, descriptors)
async function update (
_class: Ref<Class<Doc>>,
active: Ref<Viewlet> | null,
descriptors?: Ref<ViewletDescriptor>[]
): Promise<void> {
loading = true loading = true
viewlets = await client.findAll( viewlets = await client.findAll(
view.class.Viewlet, view.class.Viewlet,
@ -84,9 +113,9 @@
} }
} }
) )
const _id = getActiveViewletId()
preference = undefined preference = undefined
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0] viewlet = viewlets.find((viewlet) => viewlet._id === active) ?? viewlets[0]
setActiveViewletId(viewlet._id)
loading = false loading = false
} }

View File

@ -55,8 +55,8 @@ test.describe('recruit tests', () => {
const panel = page.locator('.popupPanel') const panel = page.locator('.popupPanel')
await panel.locator('[id="gmail\\:string\\:Email"]').scrollIntoViewIfNeeded() await panel.locator('[id="gmail\\:string\\:Email"]').scrollIntoViewIfNeeded()
await panel.locator('[id="gmail\\:string\\:Email"]').hover() await panel.locator('[id="gmail\\:string\\:Email"]').click()
await expect(page.locator(`text=${email}`).first()).toBeVisible() expect(await page.locator('.cover-channel >> input').inputValue()).toEqual(email)
}) })
test('create-application', async ({ page }) => { test('create-application', async ({ page }) => {