Fix v-btn-toggle overlap in dialogs; add segment override icon to features grid
Replace v-btn-toggle with manual button pairs in RuleGroupEditor and FeatureValueEditor — VSlideGroup (used internally) measures zero width inside teleported dialogs, causing buttons to overlap and show scroll arrows. Add donut chart icon next to feature names in the features grid when segment overrides exist; clicking opens FeatureDetail directly on the Segments tab.
This commit is contained in:
@@ -319,6 +319,7 @@ interface Props {
|
||||
modelValue: boolean;
|
||||
feature: FeatureResponse | null;
|
||||
featureState: FeatureStateResponse | null;
|
||||
initialTab?: string;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
@@ -472,7 +473,7 @@ watch(
|
||||
() => props.modelValue,
|
||||
(open) => {
|
||||
if (open) {
|
||||
activeTab.value = 'value';
|
||||
activeTab.value = props.initialTab ?? 'value';
|
||||
valueErrorMessage.value = null;
|
||||
settingsErrorMessage.value = null;
|
||||
segmentsErrorMessage.value = null;
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
<template>
|
||||
<div data-testid="feature-value-editor">
|
||||
<!-- Type selector -->
|
||||
<v-btn-toggle
|
||||
v-model="detectedMode"
|
||||
density="compact"
|
||||
data-testid="value-mode-toggle"
|
||||
>
|
||||
<v-btn value="text" size="small">Text</v-btn>
|
||||
<v-btn value="json" size="small">JSON</v-btn>
|
||||
</v-btn-toggle>
|
||||
<div class="d-flex mb-3" data-testid="value-mode-toggle">
|
||||
<v-btn
|
||||
size="small"
|
||||
:variant="detectedMode === 'text' ? 'flat' : 'outlined'"
|
||||
color="primary"
|
||||
rounded="0"
|
||||
style="border-radius: 4px 0 0 4px"
|
||||
@click="detectedMode = 'text'"
|
||||
>Text</v-btn>
|
||||
<v-btn
|
||||
size="small"
|
||||
:variant="detectedMode === 'json' ? 'flat' : 'outlined'"
|
||||
color="primary"
|
||||
rounded="0"
|
||||
style="border-radius: 0 4px 4px 0; margin-left: -1px"
|
||||
@click="detectedMode = 'json'"
|
||||
>JSON</v-btn>
|
||||
</div>
|
||||
|
||||
<!-- JSON mode -->
|
||||
<v-textarea
|
||||
@@ -83,11 +93,3 @@ const rules = {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Materio forces square icon-toggle sizing; override for text-label toggles */
|
||||
:deep(.v-btn-toggle .v-btn) {
|
||||
block-size: auto !important;
|
||||
inline-size: auto !important;
|
||||
padding-inline: 16px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
<!-- AND / OR toggle -->
|
||||
<div class="d-flex align-center ga-2 mb-3">
|
||||
<span class="text-body-2 font-weight-medium text-medium-emphasis">Match</span>
|
||||
<v-btn-toggle
|
||||
:model-value="rule.type"
|
||||
density="compact"
|
||||
mandatory
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
data-testid="rule-type-toggle"
|
||||
@update:model-value="onTypeChange"
|
||||
>
|
||||
<v-btn value="All" size="small" data-testid="rule-type-and">ALL (AND)</v-btn>
|
||||
<v-btn value="Any" size="small" data-testid="rule-type-or">ANY (OR)</v-btn>
|
||||
</v-btn-toggle>
|
||||
<div class="d-flex" data-testid="rule-type-toggle">
|
||||
<v-btn
|
||||
size="small"
|
||||
:variant="rule.type === 'All' ? 'flat' : 'outlined'"
|
||||
color="primary"
|
||||
rounded="0"
|
||||
style="border-radius: 4px 0 0 4px"
|
||||
data-testid="rule-type-and"
|
||||
@click="onTypeChange('All')"
|
||||
>ALL (AND)</v-btn>
|
||||
<v-btn
|
||||
size="small"
|
||||
:variant="rule.type === 'Any' ? 'flat' : 'outlined'"
|
||||
color="primary"
|
||||
rounded="0"
|
||||
style="border-radius: 0 4px 4px 0; margin-left: -1px"
|
||||
data-testid="rule-type-or"
|
||||
@click="onTypeChange('Any')"
|
||||
>ANY (OR)</v-btn>
|
||||
</div>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
v-if="removable"
|
||||
|
||||
@@ -53,7 +53,26 @@
|
||||
<!-- Name + description -->
|
||||
<template #item.name="{ item }: { item: FeatureResponse }">
|
||||
<div>
|
||||
<span class="text-body-2 font-weight-medium">{{ item.name }}</span>
|
||||
<div class="d-flex align-center ga-1">
|
||||
<span class="text-subtitle-1 font-weight-bold">{{ item.name }}</span>
|
||||
<v-tooltip
|
||||
v-if="(featureSegmentsMap.get(item.id) ?? []).length > 0"
|
||||
text="Has segment overrides"
|
||||
location="top"
|
||||
>
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
<v-btn
|
||||
v-bind="tooltipProps"
|
||||
icon="ri-donut-chart-fill"
|
||||
size="small"
|
||||
variant="text"
|
||||
color="secondary"
|
||||
:data-testid="`segment-override-icon-${item.id}`"
|
||||
@click.stop="openDetailOnSegments(item)"
|
||||
/>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
<p v-if="item.description" class="text-caption text-medium-emphasis mb-0">
|
||||
{{ item.description }}
|
||||
</p>
|
||||
@@ -158,6 +177,7 @@
|
||||
v-model="showDetail"
|
||||
:feature="selectedFeature"
|
||||
:feature-state="selectedFeatureState"
|
||||
:initial-tab="detailInitialTab"
|
||||
@updated="onFeatureUpdated"
|
||||
@deleted="onFeatureDeleted"
|
||||
@state-updated="onStateUpdated"
|
||||
@@ -236,6 +256,7 @@ function showError(message: string): void {
|
||||
const showDialog = ref(false);
|
||||
const showDetail = ref(false);
|
||||
const selectedFeature = ref<FeatureResponse | null>(null);
|
||||
const detailInitialTab = ref('value');
|
||||
|
||||
// Delete dialog state
|
||||
const showDeleteDialog = ref(false);
|
||||
@@ -333,6 +354,13 @@ function openCreateDialog(): void {
|
||||
}
|
||||
|
||||
function onRowClick(_event: Event, { item }: { item: FeatureResponse }): void {
|
||||
detailInitialTab.value = 'value';
|
||||
selectedFeature.value = item;
|
||||
showDetail.value = true;
|
||||
}
|
||||
|
||||
function openDetailOnSegments(item: FeatureResponse): void {
|
||||
detailInitialTab.value = 'segments';
|
||||
selectedFeature.value = item;
|
||||
showDetail.value = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user