Fix unresponsive circle icon in revert button
This commit fixes a UI bug where the circle icon of the revertbutton was unresponsive to clicks. The solution involves replacing the pseudo-element (`:before`) with an actual HTML element, enabling direct event binding. Additional improvements include: - Removal of redundant `z-index` properties to simplify click event handling and reduce complexity. - Programmatic toggle of `isChecked` on click, providing more controlled and explicit behavior and avoiding issues with native checkbox behavior, especially when overlaid on a pseudo-element.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="toggle-switch"
|
class="toggle-switch"
|
||||||
@click="handleClickPropagation"
|
@click="onClick"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
v-model="isChecked"
|
v-model="isChecked"
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
class="toggle-input"
|
class="toggle-input"
|
||||||
>
|
>
|
||||||
<div class="toggle-animation">
|
<div class="toggle-animation">
|
||||||
|
<div class="circle" />
|
||||||
<span class="label-off">{{ label }}</span>
|
<span class="label-off">{{ label }}</span>
|
||||||
<span class="label-on">{{ label }}</span>
|
<span class="label-on">{{ label }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,15 +49,16 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleClickPropagation(event: Event): void {
|
function onClick(event: Event): void {
|
||||||
if (props.stopClickPropagation) {
|
if (props.stopClickPropagation) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
isChecked.value = !isChecked.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isChecked,
|
isChecked,
|
||||||
handleClickPropagation,
|
onClick,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -114,7 +116,6 @@ $gap : 0.25em;
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
z-index: 2;
|
|
||||||
@include clickable;
|
@include clickable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +128,7 @@ $gap : 0.25em;
|
|||||||
background-color: $color-bg-unchecked;
|
background-color: $color-bg-unchecked;
|
||||||
transition: background-color 0.25s ease-out;
|
transition: background-color 0.25s ease-out;
|
||||||
|
|
||||||
&:before {
|
.circle {
|
||||||
content: "";
|
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: $padding-horizontal;
|
left: $padding-horizontal;
|
||||||
@@ -141,7 +141,6 @@ $gap : 0.25em;
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: $color-toggle-unchecked;
|
background-color: $color-toggle-unchecked;
|
||||||
transition: left 0.3s ease-out;
|
transition: left 0.3s ease-out;
|
||||||
z-index: 10;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +148,7 @@ $gap : 0.25em;
|
|||||||
background-color: $color-bg-checked;
|
background-color: $color-bg-checked;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
|
|
||||||
&:before {
|
.circle {
|
||||||
$left-offset: calc(100% - #{$size-circle});
|
$left-offset: calc(100% - #{$size-circle});
|
||||||
$padded-left-offset: calc(#{$left-offset} - #{$padding-horizontal});
|
$padded-left-offset: calc(#{$left-offset} - #{$padding-horizontal});
|
||||||
left: $padded-left-offset;
|
left: $padded-left-offset;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ describe('revert toggle', () => {
|
|||||||
.contains('revert');
|
.contains('revert');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render label completely without clipping', () => {
|
it('should render label completely without clipping', () => { // Regression test for a bug where label is partially rendered (clipped)
|
||||||
cy
|
cy
|
||||||
.get('@toggleSwitch')
|
.get('@toggleSwitch')
|
||||||
.find('span')
|
.find('span')
|
||||||
@@ -37,6 +37,22 @@ describe('revert toggle', () => {
|
|||||||
expect(expectedMinimumTextWidth).to.be.lessThan(containerWidth);
|
expect(expectedMinimumTextWidth).to.be.lessThan(containerWidth);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should toggle the revert state when clicked', () => {
|
||||||
|
cy.get('@toggleSwitch').then(($toggleSwitch) => {
|
||||||
|
// arrange
|
||||||
|
const initialState = $toggleSwitch.find('.toggle-input').is(':checked');
|
||||||
|
|
||||||
|
// act
|
||||||
|
cy.wrap($toggleSwitch).click();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
cy.wrap($toggleSwitch).find('.toggle-input').should(($input) => {
|
||||||
|
const newState = $input.is(':checked');
|
||||||
|
expect(newState).to.not.equal(initialState);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import ToggleSwitch from '@/presentation/components/Scripts/View/Tree/NodeConten
|
|||||||
const DOM_INPUT_TOGGLE_CHECKBOX_SELECTOR = 'input.toggle-input';
|
const DOM_INPUT_TOGGLE_CHECKBOX_SELECTOR = 'input.toggle-input';
|
||||||
const DOM_INPUT_TOGGLE_LABEL_OFF_SELECTOR = 'span.label-off';
|
const DOM_INPUT_TOGGLE_LABEL_OFF_SELECTOR = 'span.label-off';
|
||||||
const DOM_INPUT_TOGGLE_LABEL_ON_SELECTOR = 'span.label-on';
|
const DOM_INPUT_TOGGLE_LABEL_ON_SELECTOR = 'span.label-on';
|
||||||
|
const DOM_INPUT_CIRCLE_ICON_SELECTOR = '.circle';
|
||||||
|
|
||||||
describe('ToggleSwitch.vue', () => {
|
describe('ToggleSwitch.vue', () => {
|
||||||
describe('initial state', () => {
|
describe('initial state', () => {
|
||||||
@@ -130,10 +131,25 @@ describe('ToggleSwitch.vue', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('emits when the circle icon is clicked', async () => { // Regression test for unresponsive circle icon
|
||||||
|
// arrange
|
||||||
|
const initialCheckValue = false;
|
||||||
|
const expectedCheckValue = true;
|
||||||
|
const wrapper = mountComponent({
|
||||||
|
properties: {
|
||||||
|
modelValue: initialCheckValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// act
|
||||||
|
const circleIcon = wrapper.find(DOM_INPUT_CIRCLE_ICON_SELECTOR);
|
||||||
|
await circleIcon.trigger('click');
|
||||||
|
// assert
|
||||||
|
expect(wrapper.emitted('update:modelValue')).to.deep.equal([[expectedCheckValue]]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('click propagation', () => {
|
describe('click propagation', () => {
|
||||||
it('stops propagation `stopClickPropagation` is true', async () => {
|
it('stops propagation if `stopClickPropagation` is true', async () => {
|
||||||
// arrange
|
// arrange
|
||||||
const { wrapper: parentWrapper, parentClickEventName } = mountToggleSwitchParent(
|
const { wrapper: parentWrapper, parentClickEventName } = mountToggleSwitchParent(
|
||||||
{ stopClickPropagation: true },
|
{ stopClickPropagation: true },
|
||||||
@@ -148,7 +164,7 @@ describe('ToggleSwitch.vue', () => {
|
|||||||
const receivedEvents = parentWrapper.emitted(parentClickEventName);
|
const receivedEvents = parentWrapper.emitted(parentClickEventName);
|
||||||
expect(receivedEvents).to.equal(undefined);
|
expect(receivedEvents).to.equal(undefined);
|
||||||
});
|
});
|
||||||
it('allows propagation `stopClickPropagation` is false', async () => {
|
it('allows propagation if `stopClickPropagation` is false', async () => {
|
||||||
// arrange
|
// arrange
|
||||||
const { wrapper: parentWrapper, parentClickEventName } = mountToggleSwitchParent(
|
const { wrapper: parentWrapper, parentClickEventName } = mountToggleSwitchParent(
|
||||||
{ stopClickPropagation: false },
|
{ stopClickPropagation: false },
|
||||||
|
|||||||
Reference in New Issue
Block a user