Skip to content

Commit

Permalink
ACS-3919 - small dev fixes, unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
daemoncron committed Nov 2, 2023
1 parent e8f1971 commit 36c790a
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 48 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ Runs both unit and e2e/accesibility tests.

### Visual Regression Testing

> [!NOTE]
> this has been temporarily disabled due to Vue3 compatibility issues.
Check [backstop](https://github.com/garris/BackstopJS) for general configuration questions.

Our visual regressions audits can be performed against all patterns documented within the project's component proving grounds. To do so, follow the steps below:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"url": "https://github.com/rei/rei-cedar.git"
},
"scripts": {
"prepare": "npm run build",
"prepublishOnly": "npm-run-all lint build",
"dev": "vite",
"build": "vue-tsc && vite build && npm run build:umd && npm run build:extractcss && npm run build:icons && npm run build:docgen",
Expand Down
70 changes: 41 additions & 29 deletions src/components/modal/CdrModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@
import { debounce } from '../../utils/debounce'
import tabbable from 'tabbable';
import {
useCssModule, computed, ref, watch, onMounted, nextTick, onUnmounted, useAttrs,
useCssModule,
computed,
ref,
watch,
onMounted,
nextTick,
onUnmounted,
useAttrs,
} from 'vue';
import {
CdrBreakpointSm, CdrSpaceOneX, CdrSpaceTwoX,
CdrBreakpointSm,
CdrSpaceOneX,
CdrSpaceTwoX,
} from '@rei/cdr-tokens/dist/rei-dot-com/js/cdr-tokens.mjs';
import onTransitionEnd from './onTransitionEnd';
import CdrButton from '../button/CdrButton.vue';
Expand All @@ -14,14 +23,16 @@ import mapClasses from '../../utils/mapClasses';
/** Disruptive, action-blocking overlays used to display important information */
defineOptions({
name: 'CdrModal'
name: 'CdrModal',
inheritAttrs: false,
});
const props = defineProps({
/**
* Toggles the state of the modal
* @demoIgnore true
*/
opened: {
opened: {
type: Boolean,
required: true,
},
Expand Down Expand Up @@ -78,10 +89,8 @@ const props = defineProps({
},
});
const emits = defineEmits({
/** Fires when modal is closed */
closed: null,
});
/** Fires when modal is closed */
const emits = defineEmits({ closed: null });
const attrs = useAttrs();
const style = useCssModule();
Expand All @@ -95,8 +104,8 @@ const isOpening = ref(false);
interface offsetValues {
x: number | undefined,
y: number | undefined,
}
const offset = ref<offsetValues>({ x: undefined, y: undefined});
}
const offset = ref<offsetValues>({ x: undefined, y: undefined });
const headerHeight = ref(0);
const totalHeight = ref(0);
const scrollHeight = ref(0);
Expand Down Expand Up @@ -126,7 +135,7 @@ const onClick = (e?: Event) => {
emits('closed', e);
};
const handleKeyDown = ({ key }: { key: string}) => {
const handleKeyDown = ({ key }: { key: string }) => {
switch (key) {
case 'Escape':
case 'Esc':
Expand Down Expand Up @@ -155,13 +164,13 @@ const addNoScroll = () => {
const { documentElement, body } = document;
offset.value = {
x: window.scrollX
|| (documentElement || {}).scrollLeft
|| (body || {}).scrollLeft
|| 0,
|| (documentElement || {}).scrollLeft
|| (body || {}).scrollLeft
|| 0,
y: window.scrollY
|| (documentElement || {}).scrollTop
|| (body || {}).scrollTop
|| 0,
|| (documentElement || {}).scrollTop
|| (body || {}).scrollTop
|| 0,
};
if (documentElement) {
Expand Down Expand Up @@ -203,7 +212,8 @@ const handleOpened = () => {
lastActive = activeElement;
nextTick(() => {
if (modalEl.value) (modalEl.value as HTMLDivElement).focus(); // wrapped in if so testing error isn't thrown
// wrapped in if so testing error isn't thrown
if (modalEl.value) (modalEl.value as HTMLDivElement).focus();
measureContent();
addHandlers();
Expand Down Expand Up @@ -253,6 +263,7 @@ const dialogAttrs = computed(() => ({
'aria-describedby': props.ariaDescribedby,
id: props.id,
}));
const verticalSpace = computed(() => {
// contentWrap vertical padding
const fullscreenSpace = Number(CdrSpaceTwoX);
Expand All @@ -263,9 +274,10 @@ const verticalSpace = computed(() => {
: windowedSpace + fullscreenSpace;
// fullscreen, here, would account for outerWrap padding, which is the same CdrSpaceTwoX
});
const scrollMaxHeight = computed(() => totalHeight.value
- headerHeight.value
- verticalSpace.value);
const scrollMaxHeight = computed(() => totalHeight.value
- headerHeight.value
- verticalSpace.value);
const scrollPadding = computed(() => {
const isScrolling = scrollHeight.value > offsetHeight.value;
Expand All @@ -278,6 +290,11 @@ const scrollPadding = computed(() => {
return 0;
});
const textContentStyle = computed(() => ({
maxHeight: `${scrollMaxHeight.value}px`,
paddingRight: `${scrollPadding.value}px`,
}));
watch(() => props.opened, (newValue, oldValue) => {
if (!!newValue === !!oldValue) return;
// eslint-disable-next-line no-unused-expressions
Expand All @@ -294,18 +311,14 @@ onMounted(() => {
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>

<template>
<div
:class="mapClasses(style, baseClass, !opened ? 'cdr-modal--closed' : '')"
ref="wrapperEl"
>
<div
:class="[style['cdr-modal__outerWrap'], wrapperClass]"
>
<div :class="[style['cdr-modal__outerWrap'], wrapperClass]">
<div
aria-hidden="true"
@click="onClick"
Expand All @@ -331,7 +344,7 @@ onUnmounted(() => {
<slot name="modal">
<div
:class="[style['cdr-modal__innerWrap'], contentClass]"
:style="modalClosed ? {display: 'none'} : undefined"
:style="modalClosed ? { display: 'none' } : undefined"
>
<section>
<div :class="style['cdr-modal__content']">
Expand Down Expand Up @@ -364,7 +377,7 @@ onUnmounted(() => {

<div
:class="style['cdr-modal__text-content']"
:style="{ maxHeight: `${scrollMaxHeight}px`, paddingRight: `${scrollPadding}px`}"
:style="textContentStyle"
role="document"
ref="contentEl"
tabindex="0"
Expand All @@ -380,7 +393,6 @@ onUnmounted(() => {
<div :tabIndex="opened ? '0' : undefined" />
</div>
</div>

</template>

<style lang="scss" module src="./styles/CdrModal.module.scss">
Expand Down
106 changes: 90 additions & 16 deletions src/components/modal/__tests__/CdrModal.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { mount } from '../../../../test/vue-jest-style-workaround.js';
import CdrModal from '../CdrModal.vue';

describe('CdrModal.vue', () => {

describe('default open', ()=>{
let wrapper;
let elem;
Expand All @@ -13,7 +14,7 @@ describe('CdrModal.vue', () => {
wrapper = mount(CdrModal, {
propsData: {
opened: true,
label: "Label is the modal title"
label: 'Label is the modal title',
},
slots: {
default: 'Sticky content',
Expand All @@ -35,34 +36,107 @@ describe('CdrModal.vue', () => {
key: 'a'
});
await wrapper.vm.$nextTick();

wrapper.trigger('keydown', {
key: 'Esc',
});
await wrapper.vm.$nextTick();

wrapper.trigger('keydown', {
key: 'Escape',
});
await wrapper.vm.$nextTick();

expect(wrapper.emitted().closed.length).toBe(2);
});
});

describe('prop handling', () => {
let wrapper;
let elem;
let dialogEl;
beforeEach(()=>{
elem = document.createElement('div')
if (document.body) {
document.body.appendChild(elem)
}
wrapper = mount(CdrModal, {
propsData: {
opened: true,
label: 'Label is the modal title',
ariaDescribedby: 'some-selector',
role: 'alertdialog',
id: 'some-id',
wrapperClass: "wrapper-class",
contentClass: "content-class",
},
attrs: {
'data-ui': 'my-modal',
},
slots: {
default: 'Sticky content',
},
attachTo: elem,
});
dialogEl = wrapper.find({ref: 'modalEl'})
});

// describe('after removeNoScroll has been called', ()=>{
// beforeEach(()=>{
// wrapper.vm.removeNoScroll();
// });
it('hides title', async () => {
expect(wrapper.text()).to.include('Label is the modal title',
'it should be there on mount');
await wrapper.setProps( { showTitle: false} );
expect(wrapper.text()).to.not.include('Label is the modal title',
'now it should be gone');
});

// it('renders correctly', () => {
// expect(wrapper.element).toMatchSnapshot();
// });
it('passes aria-describedby to the dialog element', () => {
expect(dialogEl.attributes('aria-describedby')).to.equal('some-selector');
});

// it('does NOT contain the "cdr-modal__noscroll" class', () => {
// expect(document.documentElement.classList.contains('cdr-modal__noscroll')).toBeFalsy();
// expect(document.body.classList.contains('cdr-modal__noscroll')).toBeFalsy();
// });
// });
it('passes id to the dialog element', () => {
expect(dialogEl.attributes('id')).to.equal('some-id');
});

it('sets the role of the dialog if alertdialog', () => {
expect(dialogEl.attributes('role')).to.equal('alertdialog');
});

it('sets the dialog class', () => {
console.log(dialogEl.classes());
const innerWrapEl = dialogEl.find('.cdr-modal__innerWrap');
expect(innerWrapEl.classes('content-class')).to.be.true;
});

it('sets the wrapper class', () => {
const outerwrap = wrapper.find('.cdr-modal__outerWrap');
expect(outerwrap.classes('wrapper-class')).to.be.true;
});
})

describe('other variations', () => {
let elem;
beforeEach(()=>{
elem = document.createElement('div')
if (document.body) {
document.body.appendChild(elem)
}
});

it('should pass non-prop attributes to the proper child', () => {
const wrapper = mount(CdrModal, {
propsData: {
opened: true,
label: 'Label is the modal title',
},
attrs: {
'data-ui': 'my-modal',
},
attachTo: elem,
});
const dialog = wrapper.find({ref: 'modalEl'});

expect(dialog.attributes('data-ui')).to.equal('my-modal')
});
});


Expand Down
6 changes: 4 additions & 2 deletions src/components/modal/examples/Modal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@
</h2>

<cdr-modal
label="label text is title or aria"
:opened="opened"
@closed="closed"
wrapper-class="wrapper-test-class"
overlay-class="overlay-test-class"
data-backstop="modal"
role="dialog"
data-ui="hamburger-modal"
aria-labelledby="some-heading"
>
<template #title>
<cdr-text
tag="h2"
class="cdr-text-dev--heading-serif-600 modal-title"
id="some-heading"
>
Terms & Conditions
</cdr-text>
Expand Down Expand Up @@ -92,7 +94,7 @@ export default {
},
data() {
return {
opened: this.$route.name === 'Modals',
opened: false,
overflowContent: false,
override: false,
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/modal/styles/CdrModal.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ $modal-animation-duration: 150ms;
right: 0;
top: 0;
visibility: visible;
z-index: 1000;
z-index: 9999;

&__overlay {
//ITEM_DOC: Background color of the modal overlay
Expand Down

0 comments on commit 36c790a

Please sign in to comment.