Skip to content

Commit

Permalink
feat: cv-slider tests in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
OlkaB committed Aug 9, 2023
1 parent 56d7b0d commit 0bb298e
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 38 deletions.
73 changes: 36 additions & 37 deletions src/components/CvSlider/CvSlider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,11 @@ const props = defineProps({
...propsCvId,
});
const range = ref(null);
const thumb = ref(null);
const track = ref(null);
const DefaultRangeMin = 0;
const DefaultRangeMax = 100;
const MiddleValue = (getMax() + getMin()) / 2;
const DefaultValue = 0;
const StepNaNReplacement = 0;
const StepDefaultValue = 1;
Expand All @@ -163,10 +165,6 @@ const dragStartX = ref(0);
const dragStartValue = ref(0);
const percentage = ref('0%');
const range = ref(null);
const thumb = ref(null);
const track = ref(null);
const labelId = computed(() => `${cvId.value}-label`);
const internalMinLabel = computed(() => props.minLabel ?? getMin());
Expand All @@ -180,40 +178,41 @@ const rangePropsObserver = computed(() => ({
}));
const internalMultiplier = computed(() => {
let intMultiplier = parseInt(stepMultiplier);
let intMultiplier = parseInt(props.stepMultiplier);
// default to 4 fro multiplier
return isNaN(intMultiplier) ? DefaultMultiplier : Math.max(intMultiplier, 1);
});
function getRangeAttributeValue(
const getRangeAttributeValue = (
attributeName,
defaultForNaN,
defaultReturnValue
) {
) => {
if (range?.value) {
const val = parseFloat(range.value[attributeName]);
return isNaN(val) ? defaultForNaN : val;
}
return defaultReturnValue ?? defaultForNaN;
}
};
function getMin() {
const getMin = () => {
return getRangeAttributeValue('min', DefaultRangeMin);
}
};
function getMax() {
const getMax = () => {
return getRangeAttributeValue('max', DefaultRangeMax);
}
};
function getValue() {
return getRangeAttributeValue('value', DefaultValue, MiddleValue);
}
const getValue = () => {
const middleValue = (getMax() + getMin()) / 2;
return getRangeAttributeValue('value', DefaultValue, middleValue);
};
function getStep() {
const getStep = () => {
return getRangeAttributeValue('step', StepNaNReplacement, StepDefaultValue);
}
};
function setValue(newValue) {
const setValue = newValue => {
if (props.disabled) return;
range.value.value = newValue;
Expand All @@ -227,41 +226,41 @@ function setValue(newValue) {
emit('update:modelValue', rangeValue);
emit('update:value', rangeValue);
emit('change', rangeValue);
}
};
function onChange() {
const onChange = () => {
let newValue = internalValue.value.length
? parseFloat(internalValue.value)
: getMin();
setValue(newValue);
}
};
function onStartDrag(ev) {
const onStartDrag = ev => {
document.body.addEventListener('mousemove', onDrag);
document.body.addEventListener('mouseup', onStopDrag);
dragStartX.value = ev.clientX;
dragStartValue.value = getValue();
isDragging.value = true;
}
};
function onDrag(ev) {
const onDrag = ev => {
if (isDragging.value) {
// percentage change
let newValue = (ev.clientX - dragStartX.value) / track.value.offsetWidth;
// uncapped new value
newValue = dragStartValue.value + (getMax() - getMin()) * newValue;
setValue(newValue);
}
}
};
function onStopDrag() {
const onStopDrag = () => {
isDragging.value = false;
document.body.removeEventListener('mousemove', onDrag);
document.body.removeEventListener('mouseup', onStopDrag);
}
};
function onTrackClick(ev) {
const onTrackClick = ev => {
const afterAnimate = ev => {
if (ev.propertyName === 'left') {
animateClick.value = false;
Expand All @@ -275,28 +274,28 @@ function onTrackClick(ev) {
thumb.value.addEventListener('transitionend', afterAnimate);
animateClick.value = true;
setValue(newValue);
}
};
function onUpDown(ev, isUp = true) {
const onUpDown = (ev, isUp = true) => {
let curValue =
ev.target.type === 'number' ? parseFloat(ev.target.value) : getValue();
const step = getStep();
const progressValue = ev.shiftKey ? internalMultiplier.value * step : step;
const direction = isUp ? 1 : -1;
let newValue = curValue + progressValue * direction;
setValue(newValue);
}
};
function onUp(ev) {
const onUp = ev => {
onUpDown(ev);
}
};
function onDown(ev) {
const onDown = ev => {
onUpDown(ev, false);
}
};
onMounted(() => {
range.value.value = props.value;
range.value.value = props.value ?? props.modelValue;
internalValue.value = range.value.value;
percentage.value = `${
((internalValue.value - getMin()) * 100) / (getMax() - getMin())
Expand Down
149 changes: 148 additions & 1 deletion src/components/CvSlider/__tests__/CvSlider.spec.js
Original file line number Diff line number Diff line change
@@ -1 +1,148 @@
// TODO
import { shallowMount, mount } from '@vue/test-utils';

Check warning on line 1 in src/components/CvSlider/__tests__/CvSlider.spec.js

View workflow job for this annotation

GitHub Actions / lint

'mount' is defined but never used
import { nextTick } from 'vue';
import { carbonPrefix } from '../../../global/settings';
import CvSlider from '../CvSlider.vue';

const DefaultValue = '7';
const DefaultNewValue = '34';
const DefaultPropsValue = {
props: {
value: DefaultValue,
},
};
const DefaultPropsModelValue = {
props: {
modelValue: DefaultValue,
},
};

const HTMLNodesClasses = {
track: `.${carbonPrefix}--slider__track`,
valueInput: `.${carbonPrefix}--text-input`,
thumb: `.${carbonPrefix}--slider__thumb`,
};

describe('CvSlider', () => {
it('should match snapshot', async () => {
const wrapper = shallowMount(CvSlider, {
props: {
id: 'abc',
},
});
expect(wrapper.html()).toMatchSnapshot();
});

test.each([
['keydown.up', '8'],
['keydown.down', '6'],
])('handles %s press correctly', async (keyCode, expectedValue) => {
const wrapper = shallowMount(CvSlider, DefaultPropsValue);
const input = wrapper.find(HTMLNodesClasses.valueInput);
await input.trigger(keyCode);
await nextTick();

expect(wrapper.emitted('change')[0][0]).toEqual(expectedValue);
});

it('handles drag correctly', async () => {
const InitialValue = 1;
const wrapper = shallowMount(CvSlider, {
props: {
value: InitialValue.toString(),
},
});
const thumb = wrapper.find(HTMLNodesClasses.thumb);
await thumb.trigger('mousedown');
document.body.dispatchEvent(new MouseEvent('mousemove', { clientX: 100 }));
document.body.dispatchEvent(new MouseEvent('mouseup'));

await nextTick();
const emittedValue = wrapper.emitted('change')[0][0];

// Assert that value has been updated due to drag
expect(Number.isNaN(emittedValue)).toBe(false);
expect(Number(emittedValue)).toBeGreaterThan(InitialValue);
});

it('emits v-model event when modelValue prop changes', async () => {
const wrapper = shallowMount(CvSlider, DefaultPropsModelValue);

await wrapper.setProps({ modelValue: DefaultNewValue });

expect(wrapper.emitted('update:modelValue')[0][0]).toEqual(DefaultNewValue);
});

it('emits change event when value prop changes', async () => {
const wrapper = shallowMount(CvSlider, DefaultPropsValue);

await wrapper.setProps({ value: DefaultNewValue });

expect(wrapper.emitted('change')[0][0]).toEqual(DefaultNewValue);
});

test.each([
['step', '12', DefaultValue],
['min', '10', '10'],
['max', '6', '6'],
])(
'emits change event when %s prop changes',
async (propName, propValue, expectedValue) => {
const wrapper = shallowMount(CvSlider, DefaultPropsValue);

await wrapper.setProps({ [propName]: propValue });
await nextTick();

expect(wrapper.emitted('change')[0][0]).toEqual(expectedValue);
}
);

it('handles value change from input box correctly', async () => {
const wrapper = shallowMount(CvSlider);
const inputBox = wrapper.find(HTMLNodesClasses.valueInput);
await inputBox.setValue(DefaultNewValue);
await inputBox.trigger('change');

expect(wrapper.emitted('change')[0][0]).toEqual(DefaultNewValue);
});

it('respects stepMultiplier when adjusting value', async () => {
const wrapper = shallowMount(CvSlider, {
props: {
step: '3',
stepMultiplier: '5',
value: '1',
},
});

let inputBox = wrapper.find(HTMLNodesClasses.valueInput);

await inputBox.trigger('keydown.up', { shiftKey: true });
await nextTick();
await inputBox.trigger('keydown.down', { shiftKey: true });
await nextTick();

expect(wrapper.emitted('change')[0][0]).toEqual('16');
expect(wrapper.emitted('change')[1][0]).toEqual('1');
});

// it.only('updates value when track is clicked', async () => {
// const wrapper = shallowMount(CvSlider);

// const track = wrapper.find(HTMLNodesClasses.track);
// await track.trigger('click', { offsetX: 50 });

// // internalValue should be updated based on click position
// expect(wrapper.vm.internalValue).toBeGreaterThan(0);
// });

// it('handles arrow key press on thumb correctly', async () => {
// const wrapper = mount(CvSlider);
// const thumb = wrapper.find('.cv-slider__thumb');

// await thumb.trigger('keydown.up');
// expect(wrapper.vm.internalValue).toBeGreaterThan(0);

// await thumb.trigger('keydown.down');
// expect(wrapper.vm.internalValue).toBe(0);
// });
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CvSlider should match snapshot 1`] = `
<div class="cv-slider bx--form-item"><label for="abc" class="bx--label" id="abc-label" style="display: none;"></label>
<div class="bx--slider-container"><span class="bx--slider__range-label">0</span>
<div class="bx--slider" data-slider="" data-slider-input-box="#slider-input-box">
<div class="bx--slider__track"></div>
<div class="bx--slider__filled-track" style="width: 0%;"></div>
<div class="bx--slider__thumb" tabindex="0" aria-labelledby="abc-label" style="left: 0%;"></div><input id="abc" class="bx--slider__input" type="range" step="1" min="0" max="100">
</div><span class="bx--slider__range-label">100</span><input type="string" class="bx--text-input bx--slider-text-input" placeholder="0">
</div>
</div>
`;

0 comments on commit 0bb298e

Please sign in to comment.