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>
|
||||
<div
|
||||
class="toggle-switch"
|
||||
@click="handleClickPropagation"
|
||||
@click="onClick"
|
||||
>
|
||||
<input
|
||||
v-model="isChecked"
|
||||
@@ -9,6 +9,7 @@
|
||||
class="toggle-input"
|
||||
>
|
||||
<div class="toggle-animation">
|
||||
<div class="circle" />
|
||||
<span class="label-off">{{ label }}</span>
|
||||
<span class="label-on">{{ label }}</span>
|
||||
</div>
|
||||
@@ -48,15 +49,16 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
|
||||
function handleClickPropagation(event: Event): void {
|
||||
function onClick(event: Event): void {
|
||||
if (props.stopClickPropagation) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
isChecked.value = !isChecked.value;
|
||||
}
|
||||
|
||||
return {
|
||||
isChecked,
|
||||
handleClickPropagation,
|
||||
onClick,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -114,7 +116,6 @@ $gap : 0.25em;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
z-index: 2;
|
||||
@include clickable;
|
||||
}
|
||||
|
||||
@@ -127,8 +128,7 @@ $gap : 0.25em;
|
||||
background-color: $color-bg-unchecked;
|
||||
transition: background-color 0.25s ease-out;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
.circle {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: $padding-horizontal;
|
||||
@@ -141,7 +141,6 @@ $gap : 0.25em;
|
||||
border-radius: 50%;
|
||||
background-color: $color-toggle-unchecked;
|
||||
transition: left 0.3s ease-out;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +148,7 @@ $gap : 0.25em;
|
||||
background-color: $color-bg-checked;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
&:before {
|
||||
.circle {
|
||||
$left-offset: calc(100% - #{$size-circle});
|
||||
$padded-left-offset: calc(#{$left-offset} - #{$padding-horizontal});
|
||||
left: $padded-left-offset;
|
||||
|
||||
@@ -24,7 +24,7 @@ describe('revert toggle', () => {
|
||||
.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
|
||||
.get('@toggleSwitch')
|
||||
.find('span')
|
||||
@@ -37,6 +37,22 @@ describe('revert toggle', () => {
|
||||
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_LABEL_OFF_SELECTOR = 'span.label-off';
|
||||
const DOM_INPUT_TOGGLE_LABEL_ON_SELECTOR = 'span.label-on';
|
||||
const DOM_INPUT_CIRCLE_ICON_SELECTOR = '.circle';
|
||||
|
||||
describe('ToggleSwitch.vue', () => {
|
||||
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', () => {
|
||||
it('stops propagation `stopClickPropagation` is true', async () => {
|
||||
it('stops propagation if `stopClickPropagation` is true', async () => {
|
||||
// arrange
|
||||
const { wrapper: parentWrapper, parentClickEventName } = mountToggleSwitchParent(
|
||||
{ stopClickPropagation: true },
|
||||
@@ -148,7 +164,7 @@ describe('ToggleSwitch.vue', () => {
|
||||
const receivedEvents = parentWrapper.emitted(parentClickEventName);
|
||||
expect(receivedEvents).to.equal(undefined);
|
||||
});
|
||||
it('allows propagation `stopClickPropagation` is false', async () => {
|
||||
it('allows propagation if `stopClickPropagation` is false', async () => {
|
||||
// arrange
|
||||
const { wrapper: parentWrapper, parentClickEventName } = mountToggleSwitchParent(
|
||||
{ stopClickPropagation: false },
|
||||
|
||||
Reference in New Issue
Block a user