Skip to content

Commit

Permalink
Merge branch 'release/1.0.0-alpha.7' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
titouanmathis committed Aug 4, 2020
2 parents 55a6aa4 + e971ff5 commit 0213630
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 104 deletions.
99 changes: 32 additions & 67 deletions demo/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
<body>
<main data-breakpoint class="relative p-10" @click="foo">
<div data-component="MediaQuery" data-active-breakpoints="l">
<div data-component="Modal" data-options='{ "debug": false, "styles": { "modal": { "active": "transition-all duration-500" } } }' class="text-center">
<div
data-component="Modal"
data-options='{ "debug": false, "styles": { "modal": { "active": "transition-all duration-500" } } }'
class="text-center"
>
<!--
Modal opening trigger.
This ref will be used to open the modal on click.
Expand Down Expand Up @@ -86,17 +90,13 @@
<div
data-component="AccordionItem"
class="border -mb-px"
data-options='{ "enterActive": "transition-all duration-500 ease-out-expo", "leaveActive": "transition-all duration-300 ease-out-expo"}'
data-options='{ "styles": { "container": { "active": "transition-all duration-500 ease-out-expo" } } }'
>
<button data-ref="btn" class="block w-full p-4 text-left cursor-pointer">
Lorem ipsum
</button>
<div
data-ref="container"
class="relative h-0 invisible overflow-hidden"
aria-hidden="true"
>
<div data-ref="content" class="absolute top-0 left-0 w-full p-4 pt-0">
<div data-ref="container" class="relative overflow-hidden" aria-hidden="true">
<div data-ref="content" class="w-full p-4 pt-0">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto nobis laboriosam
rerum aspernatur deserunt similique facere impedit alias, error officia cum quibusdam
quisquam quo, magnam libero eius provident dolorum molestiae.
Expand All @@ -106,17 +106,13 @@
<div
data-component="AccordionItem"
class="border -mb-px"
data-options='{ "enterActive": "transition-all duration-500 ease-out-expo", "leaveActive": "transition-all duration-300 ease-out-expo"}'
data-options='{ "styles": { "container": { "active": "transition-all duration-500 ease-out-expo" } } }'
>
<button data-ref="btn" class="block w-full p-4 text-left cursor-pointer">
Lorem ipsum
</button>
<div
data-ref="container"
class="relative h-0 invisible overflow-hidden"
aria-hidden="true"
>
<div data-ref="content" class="absolute top-0 left-0 w-full p-4 pt-0">
<div data-ref="container" class="relative overflow-hidden" aria-hidden="true">
<div data-ref="content" class="w-full p-4 pt-0">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto nobis laboriosam
rerum aspernatur deserunt similique facere impedit alias, error officia cum quibusdam
quisquam quo, magnam libero eius provident dolorum molestiae.
Expand All @@ -126,17 +122,13 @@
<div
data-component="AccordionItem"
class="border -mb-px"
data-options='{ "enterActive": "transition-all duration-500 ease-out-expo", "leaveActive": "transition-all duration-300 ease-out-expo"}'
data-options='{ "styles": { "container": { "active": "transition-all duration-500 ease-out-expo" } } }'
>
<button data-ref="btn" class="block w-full p-4 text-left cursor-pointer">
Lorem ipsum
</button>
<div
data-ref="container"
class="relative h-0 invisible overflow-hidden"
aria-hidden="true"
>
<div data-ref="content" class="absolute top-0 left-0 w-full p-4 pt-0">
<div data-ref="container" class="relative overflow-hidden" aria-hidden="true">
<div data-ref="content" class="w-full p-4 pt-0">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto nobis laboriosam
rerum aspernatur deserunt similique facere impedit alias, error officia cum quibusdam
quisquam quo, magnam libero eius provident dolorum molestiae.
Expand All @@ -153,72 +145,45 @@
<div
data-component="AccordionItem"
class="border -mb-px"
data-options='{ "active": "transition-all duration-500 ease-out-expo" }'
data-options='{ "styles": { "container": { "active": "transition-all duration-500 ease-out-expo" }, "icon": { "open": "transform rotate-180", "active": "transition duration-500 ease-out-expo" } } }'
>
<button data-ref="btn" class="block w-full p-4 text-left cursor-pointer">
<button data-ref="btn" class="flex justify-between w-full p-4 text-left cursor-pointer">
Show image #1
<span class="inline-block" data-ref="icon"></span>
</button>
<div
data-ref="container"
class="relative h-0 invisible overflow-hidden"
aria-hidden="true"
>
<div data-ref="content" class="absolute top-0 left-0 w-full p-4 pt-0">
<img
class="lazyload"
data-src="https://placekitten.com/200/100"
width="200"
height="100"
alt=""
/>
<div data-ref="container" class="relative overflow-hidden" aria-hidden="true">
<div data-ref="content" class="w-full p-4 pt-0">
<img src="https://placekitten.com/200/100" width="200" height="100" alt="" />
</div>
</div>
</div>
<div
data-component="AccordionItem"
class="border -mb-px"
data-options='{ "active": "transition-all duration-500 ease-out-expo" }'
data-options='{ "styles": { "container": { "active": "transition-all duration-500 ease-out-expo" }, "icon": { "open": "transform rotate-180", "active": "transition duration-500 ease-out-expo" } } }'
>
<button data-ref="btn" class="block w-full p-4 text-left cursor-pointer">
<button data-ref="btn" class="flex justify-between w-full p-4 text-left cursor-pointer">
Show image #2
<span class="inline-block" data-ref="icon"></span>
</button>
<div
data-ref="container"
class="relative h-0 invisible overflow-hidden"
aria-hidden="true"
>
<div data-ref="content" class="absolute top-0 left-0 w-full p-4 pt-0">
<img
class="lazyload"
data-src="https://placekitten.com/200/120"
width="200"
height="120"
alt=""
/>
<div data-ref="container" class="relative overflow-hidden" aria-hidden="true">
<div data-ref="content" class="w-full p-4 pt-0">
<img src="https://placekitten.com/200/120" width="200" height="120" alt="" />
</div>
</div>
</div>
<div
data-component="AccordionItem"
class="border -mb-px"
data-options='{ "active": "transition-all duration-500 ease-out-expo" }'
data-options='{ "styles": { "container": { "active": "transition-all duration-500 ease-out-expo" }, "icon": { "open": "transform rotate-180", "active": "transition duration-500 ease-out-expo" } } }'
>
<button data-ref="btn" class="block w-full p-4 text-left cursor-pointer">
<button data-ref="btn" class="flex justify-between w-full p-4 text-left cursor-pointer">
Show image #3
<span class="inline-block" data-ref="icon"></span>
</button>
<div
data-ref="container"
class="relative h-0 invisible overflow-hidden"
aria-hidden="true"
>
<div data-ref="content" class="absolute top-0 left-0 w-full p-4 pt-0">
<img
class="lazyload"
data-src="https://placekitten.com/200/80"
width="200"
height="80"
alt=""
/>
<div data-ref="container" class="relative overflow-hidden" aria-hidden="true">
<div data-ref="content" class="w-full p-4 pt-0">
<img src="https://placekitten.com/200/80" width="200" height="80" alt="" />
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@studiometa/js-toolkit",
"version": "1.0.0-alpha.6",
"version": "1.0.0-alpha.7",
"description": "",
"scripts": {
"prepublishOnly": "if [[ ! $PWD =~ dist$ ]]; then echo '🚧 Use `npm run deploy` instead of `npm publish`!' && echo '' && exit 1; fi",
Expand Down
101 changes: 77 additions & 24 deletions src/components/Accordion/AccordionItem.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Base from '../../abstracts/Base';
import * as classes from '../../utils/css/classes';
import transition from '../../utils/css/transition';
import * as styles from '../../utils/css/styles';
import transition, { setClassesOrStyles } from '../../utils/css/transition';

/**
* AccordionItem class.
Expand All @@ -13,9 +13,13 @@ export default class AccordionItem extends Base {
get config() {
return {
name: 'AccordionItem',
active: '',
enterActive: '',
leaveActive: '',
styles: {
container: {
open: '',
active: '',
closed: '',
},
},
};
}

Expand All @@ -26,6 +30,7 @@ export default class AccordionItem extends Base {
mounted() {
this.$refs.btn.setAttribute('id', this.$id);
this.$refs.content.setAttribute('aria-labelledby', this.$id);
styles.add(this.$refs.container, { visibility: 'invisible', height: 0 });
}

/**
Expand Down Expand Up @@ -55,16 +60,40 @@ export default class AccordionItem extends Base {
this.isOpen = true;
this.$refs.container.setAttribute('aria-hidden', 'false');

classes.remove(this.$refs.container, 'invisible');
const height = this.$refs.content.offsetHeight;
await transition(this.$refs.container, {
from: { height: 0 },
active: this.$options.enterActive || this.$options.active,
to: { height: `${height}px` },
});
styles.remove(this.$refs.container, { visibility: 'invisible' });
const { container, ...otherStyles } = this.$options.styles;

classes.remove(this.$refs.container, 'h-0');
classes.remove(this.$refs.content, 'absolute');
await Promise.all([
transition(this.$refs.container, {
from: { height: 0 },
active: container.active,
to: { height: `${this.$refs.content.offsetHeight}px` },
}).then(() => {
// Remove style only if the item has not been closed before the end
if (this.isOpen) {
styles.remove(this.$refs.content, { position: 'absolute' });
}

return Promise.resolve();
}),
...Object.entries(otherStyles)
.filter(([refName]) => this.$refs[refName])
.map(([refName, { open, active, closed } = {}]) =>
transition(this.$refs[refName], {
from: closed,
active,
to: open,
}).then(() => {
// Set style only if the item has not been closed before the end
// Do nothing if the item has been closed before the end
if (this.isOpen) {
setClassesOrStyles(this.$refs[refName], open);
}

return Promise.resolve();
})
),
]);
}

/**
Expand All @@ -81,15 +110,39 @@ export default class AccordionItem extends Base {

this.isOpen = false;

const height = this.$refs.content.offsetHeight;
classes.add(this.$refs.content, 'absolute');
await transition(this.$refs.container, {
from: { height: `${height}px` },
active: this.$options.leaveActive || this.$options.active,
to: 'h-0',
});
classes.add(this.$refs.container, 'h-0');
classes.add(this.$refs.container, 'invisible');
this.$refs.container.setAttribute('aria-hidden', 'true');
const height = this.$refs.container.offsetHeight;
styles.add(this.$refs.content, { position: 'absolute' });
const { container, ...otherStyles } = this.$options.styles;

await Promise.all([
transition(this.$refs.container, {
from: { height: `${height}px` },
active: container.active,
to: { height: 0 },
}).then(() => {
// Add end styles only if the item has not been re-opened before the end
if (!this.isOpen) {
styles.add(this.$refs.container, { height: 0, visibility: 'invisible' });
this.$refs.container.setAttribute('aria-hidden', 'true');
}
return Promise.resolve();
}),
...Object.entries(otherStyles)
.filter(([refName]) => this.$refs[refName])
.map(([refName, { open, active, closed } = {}]) =>
transition(this.$refs[refName], {
from: open,
active,
to: closed,
}).then(() => {
// Add end styles only if the item has not been re-opened before the end
if (!this.isOpen) {
setClassesOrStyles(this.$refs[refName], closed);
}

return Promise.resolve();
})
),
]);
}
}
1 change: 0 additions & 1 deletion src/utils/css/transition.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ export default async function transition(element, name) {
// End any previous transition running on the element.
if (element.__isTransitioning__) {
end(element, classesOrStyles);
await nextFrame();
}

await start(element, classesOrStyles);
Expand Down
42 changes: 32 additions & 10 deletions tests/components/Accordion/AccordionItem.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,35 @@ describe('AccordionItem component', () => {
let btn;
let container;
let content;
let icon;

beforeEach(() => {
document.body.innerHTML = `
<div data-component="AccordionItem">
<button data-ref="btn">
Button
</button>
<div data-ref="container">
<div data-ref="content">
Content
</div>
</div>
</div>
<div
data-component="AccordionItem"
data-options='{
"styles": {
"icon": {
"open": "transform rotate-180",
"active": { "transition": "all 1s linear" },
"closed": "transform rotate-0"
}
}
}'>
<button data-ref="btn">
Button
<span data-ref="icon">▼</span>
</button>
<div data-ref="container">
<div data-ref="content">Content</div>
</div>
</div>;
`;
item = new AccordionItem(document.body.firstElementChild);
btn = document.querySelector('[data-ref="btn"]');
container = document.querySelector('[data-ref="container"]');
content = document.querySelector('[data-ref="content"]');
icon = document.querySelector('[data-ref="icon"]');
});

it('should had aria-attributes when mounted', () => {
Expand All @@ -32,16 +43,27 @@ describe('AccordionItem component', () => {
});

it('should open and close', async () => {
const spy = jest.spyOn(icon.classList, 'add');
await item.open();
expect(container.getAttribute('aria-hidden')).toBe('false');
expect(spy).toHaveBeenLastCalledWith('rotate-180');
await item.close();
expect(container.getAttribute('aria-hidden')).toBe('true');
expect(spy).toHaveBeenLastCalledWith('rotate-0');
btn.click();
await wait(100);
expect(container.getAttribute('aria-hidden')).toBe('false');
btn.click();
await wait(100);
expect(container.getAttribute('aria-hidden')).toBe('true');

item.open();
item.close();
expect(spy).toHaveBeenLastCalledWith('rotate-180');
await item.open();
item.close();
item.open();
expect(spy).toHaveBeenLastCalledWith('rotate-0');
});

it('should emit open and close events', async () => {
Expand Down

0 comments on commit 0213630

Please sign in to comment.