fix: test case table ux

This commit is contained in:
Jannat Patel
2025-11-14 17:46:31 +05:30
parent d86fd0f6f6
commit a481bcd974
2 changed files with 51 additions and 26 deletions

View File

@@ -3,7 +3,7 @@
<div class="text-xs text-ink-gray-5 mb-2"> <div class="text-xs text-ink-gray-5 mb-2">
{{ label }} {{ label }}
</div> </div>
<div class="overflow-x-auto border rounded-md"> <div class="overflow-x-auto overflow-y-visible border rounded-md">
<div <div
class="grid items-center space-x-4 p-2 border-b" class="grid items-center space-x-4 p-2 border-b"
:style="{ gridTemplateColumns: getGridTemplateColumns() }" :style="{ gridTemplateColumns: getGridTemplateColumns() }"
@@ -27,7 +27,7 @@
<input <input
v-if="showKey(key)" v-if="showKey(key)"
v-model="row[key]" v-model="row[key]"
class="py-1.5 px-2 border-none focus:ring-0 focus:border focus:border-gray-300 focus:bg-surface-gray-2 rounded-sm text-sm focus:outline-none" class="py-1.5 px-2 border-none focus:ring-0 focus:border focus:border-gray-300 focus:bg-surface-gray-2 rounded-md text-sm focus:outline-none"
/> />
</template> </template>
@@ -43,20 +43,27 @@
</template> </template>
</Button> </Button>
<div <Teleport to="body">
v-if="menuOpenIndex === rowIndex" <div
class="absolute right-[30px] top-5 mt-1 w-32 bg-surface-white border border-outline-gray-1 rounded-md shadow-sm" v-if="menuOpenIndex === rowIndex"
> :style="{
<button position: 'absolute',
@click="deleteRow(rowIndex)" top: menuTopPosition,
class="flex items-center space-x-2 w-full text-left px-3 py-2 text-sm text-ink-red-3" left: menuLeftPosition,
}"
class="top-5 mt-1 w-32 bg-surface-white border border-outline-gray-1 rounded-md shadow-sm"
> >
<Trash2 class="size-4 stroke-1.5" /> <button
<span> @click="deleteRow(rowIndex)"
{{ __('Delete') }} class="flex items-center space-x-2 w-full text-left px-3 py-2 text-sm text-ink-red-3"
</span> >
</button> <Trash2 class="size-4 stroke-1.5" />
</div> <span>
{{ __('Delete') }}
</span>
</button>
</div>
</Teleport>
</div> </div>
</div> </div>
</div> </div>
@@ -73,17 +80,19 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue' import { nextTick, ref, watch } from 'vue'
import { Button } from 'frappe-ui' import { Button } from 'frappe-ui'
import { Ellipsis, Plus, Trash2 } from 'lucide-vue-next' import { Ellipsis, Plus, Trash2 } from 'lucide-vue-next'
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from '@vueuse/core'
const rows = defineModel<Cell[][]>() const rows = defineModel<Record<string, string>[]>()
const menuRef = ref(null) const menuRef = ref(null)
const menuOpenIndex = ref<number | null>(null) const menuOpenIndex = ref<number | null>(null)
const menuTopPosition = ref<string>('') const menuTopPosition = ref<string>('')
const menuLeftPosition = ref('0px')
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: Cell[][]): void (e: 'update:modelValue', value: Record<string, string>[]): void
}>() }>()
type Cell = { type Cell = {
@@ -93,19 +102,19 @@ type Cell = {
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
modelValue?: Cell[][] modelValue?: Record<string, string>[]
columns?: string[] columns?: string[]
label?: string label?: string
}>(), }>(),
{ {
columns: [], columns: () => [] as string[],
} }
) )
const columns = ref(props.columns) const columns = ref(props.columns)
watch(rows, () => { watch(rows, () => {
if (rows.value?.length < 1) { if (rows.value && rows.value.length < 1) {
addRow() addRow()
} }
}) })
@@ -119,12 +128,25 @@ const addRow = () => {
newRow[column.toLowerCase().split(' ').join('_')] = '' newRow[column.toLowerCase().split(' ').join('_')] = ''
}) })
rows.value.push(newRow) rows.value.push(newRow)
focusNewRowInput()
emit('update:modelValue', rows.value) emit('update:modelValue', rows.value)
} }
const focusNewRowInput = () => {
nextTick(() => {
const rowElements = document.querySelectorAll('.overflow-x-auto .grid')[
rows.value!.length
]
const firstInput = rowElements.querySelector('input')
if (firstInput) {
;(firstInput as HTMLInputElement).focus()
}
})
}
const deleteRow = (index: number) => { const deleteRow = (index: number) => {
rows.value.splice(index, 1) rows.value?.splice(index, 1)
emit('update:modelValue', rows.value) emit('update:modelValue', rows.value ?? [])
} }
const getGridTemplateColumns = () => { const getGridTemplateColumns = () => {
@@ -132,8 +154,10 @@ const getGridTemplateColumns = () => {
} }
const toggleMenu = (index: number, event: MouseEvent) => { const toggleMenu = (index: number, event: MouseEvent) => {
menuOpenIndex.value = menuOpenIndex.value === index ? null : index const rect = (event.target as HTMLElement).getBoundingClientRect()
menuTopPosition.value = `${event.clientY + 10}px` menuOpenIndex.value = index
menuTopPosition.value = rect.bottom + 'px'
menuLeftPosition.value = rect.right + 'px'
} }
onClickOutside(menuRef, () => { onClickOutside(menuRef, () => {

View File

@@ -139,7 +139,8 @@ function submitEvaluation(close) {
close() close()
}, },
onError(err) { onError(err) {
toast.warning(__(err.messages?.[0] || err)) console.log(err.messages?.[0] || err)
toast.warning(__(err.messages?.[0] || err), { duration: 10000 })
}, },
}) })
} }