Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Issue-126] :: Implement Disclosure component #168

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ember-headlessui/addon/components/disclosure.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<div
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need the wrapping div?

data-test-ember-headlessui-disclosure-wrapper
class='ember-headlessui-disclosure-wrapper'
>
{{yield
(hash
isOpen=this.isOpen
Button=(component
'disclosure/button' toggleState=this.toggleState isOpen=this.isOpen
)
Panel=(component 'disclosure/panel' isOpen=this.isOpen)
)
}}
</div>
15 changes: 15 additions & 0 deletions ember-headlessui/addon/components/disclosure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Component from '@glimmer/component';

import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class Disclosure extends Component {

@tracked isOpen = false;

@action
toggleState(){
this.isOpen = !this.isOpen;
}

}
12 changes: 12 additions & 0 deletions ember-headlessui/addon/components/disclosure/button.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<button
type='button'
aria-haspopup={{true}}
aria-controls={{if @isOpen this.itemsGuid}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we generate a single GUID in the parent component, just to reduce the number of repeat guid creations?

passing a guid (and then concatting suffixes) allows us to then also get rid of data-test-selectors (we don't want to ship these to consumers either, but that's a current problem all over the addon that we need to fix -- out of scope for this PR)

aria-expanded={{@isOpen}}
data-test-headlessui-disclosure-button='test-disclosure-btn-{{@index}}'
id={{this.buttonGuid}}
{{on 'click' @toggleState}}
...attributes
>
{{yield}}
</button>
19 changes: 19 additions & 0 deletions ember-headlessui/addon/components/disclosure/button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Component from '@glimmer/component';

import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { guidFor } from '@ember/object/internals';


export default class Button extends Component {
guid = `${guidFor(this)}-tailwindui-disclosure-button`;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use the guid helper instead of having a backing class?

template-only components are more performant



get itemsGuid() {
return `${this.guid}-items`;
}

get buttonGuid() {
return `${this.guid}-button`;
}
}
9 changes: 9 additions & 0 deletions ember-headlessui/addon/components/disclosure/panel.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{#if @isOpen}}
<div
id={{this.panelGuid}}
data-test-headlessui-disclosure-panel='disclosure-panel-{{@index}}'
...attributes
>
{{yield}}
</div>
{{/if}}
10 changes: 10 additions & 0 deletions ember-headlessui/addon/components/disclosure/panel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Component from '@glimmer/component';

import { tracked } from '@glimmer/tracking';
import { guidFor } from '@ember/object/internals';

export default class Panel extends Component {
panelGuid = `${guidFor(this)}-tailwindui-disclosure-panel`;

@tracked isOpen = false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does this have its own isOpen state when the parent component also has isOpen?

}
1 change: 1 addition & 0 deletions ember-headlessui/app/components/disclosure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'ember-headlessui/components/disclosure';
1 change: 1 addition & 0 deletions ember-headlessui/app/components/disclosure/button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'ember-headlessui/components/disclosure/button';
1 change: 1 addition & 0 deletions ember-headlessui/app/components/disclosure/panel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'ember-headlessui/components/disclosure/panel';
61 changes: 61 additions & 0 deletions test-app/app/components/disclosure/basic.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<div class='w-full px-4 py-4 pt-16'>
<div class='mx-auto w-full max-w-md bg-white shadow-md px-4 py-4 rounded-lg'>
<Disclosure as |disclosure|>
<span class='rounded-md shadow-sm'>
<disclosure.Button
@index={{1}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do we have index here? A consumer should never need to manage the index, yeah?

class='flex mt-2 w-full justify-between rounded-lg bg-indigo-500 px-4 py-2 text-left text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 pb-2 mb-2'
>
<span>What is your refund policy?</span>
<svg
class='w-5 h-5 ml-2 -mr-1
{{if disclosure.isOpen "transform rotate-180"}}'
viewBox='0 0 20 20'
fill='currentColor'
>
<path
fillRule='evenodd'
d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z'
clipRule='evenodd'
></path>
</svg>
</disclosure.Button>
<disclosure.Panel
@index={{1}}
class='px-4 pt-4 pb-4 text-sm text-gray-500'
>
If you're unhappy with your purchase for any reason, email us within
90 days and we'll refund you in full, no questions asked.
</disclosure.Panel>
</span>
</Disclosure>
<Disclosure as |disclosure|>
<span class='rounded-md shadow-sm'>
<disclosure.Button
@index={{2}}
class='flex mt-2 w-full justify-between rounded-lg bg-indigo-500 px-4 py-2 text-left text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 pb-2 mb-2'
>
<span>Do you provide technical support ?</span>
<svg
class='w-5 h-5 ml-2 -mr-1
{{if disclosure.isOpen "transform rotate-180"}}'
viewBox='0 0 20 20'
fill='currentColor'
>
<path
fillRule='evenodd'
d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z'
clipRule='evenodd'
></path>
</svg>
</disclosure.Button>
<disclosure.Panel
@index={{2}}
class='px-4 pt-4 pb-4 text-sm text-gray-500'
>
No.
</disclosure.Panel>
</span>
</Disclosure>
</div>
</div>
4 changes: 4 additions & 0 deletions test-app/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ Router.map(function () {
this.route('radio-group', function () {
this.route('radio-group-basic');
});

this.route('disclosure', function () {
this.route('disclosure-basic');
});
});
1 change: 1 addition & 0 deletions test-app/app/templates/disclosure/disclosure-basic.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<Disclosure::Basic />
12 changes: 12 additions & 0 deletions test-app/app/templates/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,18 @@
</li>
</ul>
</li>
<li>
<h3 class='text-xl'>
Disclosure
</h3>
<ul>
<li>
<LinkTo @route='disclosure.disclosure-basic'>
Disclosure (basic)
</LinkTo>
</li>
</ul>
</li>
</ul>
</div>
</div>
170 changes: 170 additions & 0 deletions test-app/tests/integration/components/disclosure-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { hbs } from 'ember-cli-htmlbars';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { click, render} from '@ember/test-helpers';

module('Integration | Component | <Disclosure>', (hooks) => {
setupRenderingTest(hooks);

test('it should render disclosure component', async function (assert) {
await render(hbs`
<Disclosure as |disclosure|>
<span class='rounded-md shadow-sm'>
<disclosure.Button @index={{1}}
class='flex mt-2 w-full justify-between rounded-lg bg-indigo-500 px-4 py-2 text-left text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 pb-2 mb-2'>
<span>What is your refund policy?</span>
<svg class='w-5 h-5 ml-2 -mr-1
{{if disclosure.isOpen "transform rotate-180"}}' viewBox='0 0 20 20' fill='currentColor'>
<path fillRule='evenodd'
d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z'
clipRule='evenodd'></path>
</svg>
</disclosure.Button>
<disclosure.Panel @index={{1}} class='px-4 pt-4 pb-4 text-sm text-gray-500'>
If you're unhappy with your purchase for any reason, email us within
90 days and we'll refund you in full, no questions asked.
</disclosure.Panel>
</span>
</Disclosure>
`);
assert.dom('[data-test-ember-headlessui-disclosure-wrapper]').exists();
});

test('it should render disclosure component titles', async function (assert) {
await render(hbs`
<Disclosure as |disclosure|>
<span class='rounded-md shadow-sm'>
<disclosure.Button @index={{1}}
class='flex mt-2 w-full justify-between rounded-lg bg-indigo-500 px-4 py-2 text-left text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 pb-2 mb-2'>
<span>What is your refund policy?</span>
<svg class='w-5 h-5 ml-2 -mr-1
{{if disclosure.isOpen "transform rotate-180"}}' viewBox='0 0 20 20' fill='currentColor'>
<path fillRule='evenodd'
d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z'
clipRule='evenodd'></path>
</svg>
</disclosure.Button>
<disclosure.Panel @index={{1}} class='px-4 pt-4 pb-4 text-sm text-gray-500'>
If you're unhappy with your purchase for any reason, email us within
90 days and we'll refund you in full, no questions asked.
</disclosure.Panel>
</span>
</Disclosure>
<Disclosure as |disclosure|>
<span class='rounded-md shadow-sm'>
<disclosure.Button @index={{2}}
class='flex mt-2 w-full justify-between rounded-lg bg-indigo-500 px-4 py-2 text-left text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 pb-2 mb-2'>
<span>Do you provide technical support ?</span>
<svg class='w-5 h-5 ml-2 -mr-1
{{if disclosure.isOpen "transform rotate-180"}}' viewBox='0 0 20 20' fill='currentColor'>
<path fillRule='evenodd'
d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z'
clipRule='evenodd'></path>
</svg>
</disclosure.Button>
<disclosure.Panel @index={{2}} class='px-4 pt-4 pb-4 text-sm text-gray-500'>
No
</disclosure.Panel>
</span>
</Disclosure>
`);
assert.dom('[data-test-headlessui-disclosure-button="test-disclosure-btn-1"]').hasText('What is your refund policy?')
assert.dom('[data-test-headlessui-disclosure-button="test-disclosure-btn-2"]').hasText('Do you provide technical support ?')
});

test('it should render disclosure component panel description', async function (assert) {
await render(hbs`
<Disclosure as |disclosure|>
<span class='rounded-md shadow-sm'>
<disclosure.Button @index={{1}}
class='flex mt-2 w-full justify-between rounded-lg bg-indigo-500 px-4 py-2 text-left text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 pb-2 mb-2'>
<span>What is your refund policy?</span>
<svg class='w-5 h-5 ml-2 -mr-1
{{if disclosure.isOpen "transform rotate-180"}}' viewBox='0 0 20 20' fill='currentColor'>
<path fillRule='evenodd'
d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z'
clipRule='evenodd'></path>
</svg>
</disclosure.Button>
<disclosure.Panel @index={{1}} class='px-4 pt-4 pb-4 text-sm text-gray-500'>
If you're unhappy with your purchase for any reason, email us within
90 days and we'll refund you in full, no questions asked.
</disclosure.Panel>
</span>
</Disclosure>
<Disclosure as |disclosure|>
<span class='rounded-md shadow-sm'>
<disclosure.Button @index={{2}}
class='flex mt-2 w-full justify-between rounded-lg bg-indigo-500 px-4 py-2 text-left text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 pb-2 mb-2'>
<span>Do you provide technical support ?</span>
<svg class='w-5 h-5 ml-2 -mr-1
{{if disclosure.isOpen "transform rotate-180"}}' viewBox='0 0 20 20' fill='currentColor'>
<path fillRule='evenodd'
d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z'
clipRule='evenodd'></path>
</svg>
</disclosure.Button>
<disclosure.Panel @index={{2}} class='px-4 pt-4 pb-4 text-sm text-gray-500'>
No
</disclosure.Panel>
</span>
</Disclosure>
`);
await click('[data-test-headlessui-disclosure-button="test-disclosure-btn-1"]');
await click('[data-test-headlessui-disclosure-button="test-disclosure-btn-2"]');
assert.dom('[data-test-headlessui-disclosure-panel="disclosure-panel-1"]').hasText(`If you're unhappy with your purchase for any reason, email us within
90 days and we'll refund you in full, no questions asked.`);
assert.dom('[data-test-headlessui-disclosure-panel="disclosure-panel-2"]').hasText('No');
});

test('check the toggle for disclosure component', async function (assert) {
await render(hbs`
<Disclosure as |disclosure|>
<span class='rounded-md shadow-sm'>
<disclosure.Button @index={{1}}
class='flex mt-2 w-full justify-between rounded-lg bg-indigo-500 px-4 py-2 text-left text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 pb-2 mb-2'>
<span>What is your refund policy?</span>
<svg class='w-5 h-5 ml-2 -mr-1
{{if disclosure.isOpen "transform rotate-180"}}' viewBox='0 0 20 20' fill='currentColor'>
<path fillRule='evenodd'
d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z'
clipRule='evenodd'></path>
</svg>
</disclosure.Button>
<disclosure.Panel @index={{1}} class='px-4 pt-4 pb-4 text-sm text-gray-500'>
If you're unhappy with your purchase for any reason, email us within
90 days and we'll refund you in full, no questions asked.
</disclosure.Panel>
</span>
</Disclosure>
<Disclosure as |disclosure|>
<span class='rounded-md shadow-sm'>
<disclosure.Button @index={{2}}
class='flex mt-2 w-full justify-between rounded-lg bg-indigo-500 px-4 py-2 text-left text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 pb-2 mb-2'>
<span>Do you provide technical support ?</span>
<svg class='w-5 h-5 ml-2 -mr-1
{{if disclosure.isOpen "transform rotate-180"}}' viewBox='0 0 20 20' fill='currentColor'>
<path fillRule='evenodd'
d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z'
clipRule='evenodd'></path>
</svg>
</disclosure.Button>
<disclosure.Panel @index={{2}} class='px-4 pt-4 pb-4 text-sm text-gray-500'>
No
</disclosure.Panel>
</span>
</Disclosure>
`);
assert.dom('[data-test-headlessui-disclosure-panel="disclosure-panel-1"]').doesNotExist();
await click('[data-test-headlessui-disclosure-button="test-disclosure-btn-1"]');
assert.dom('[data-test-headlessui-disclosure-panel="disclosure-panel-1"]').exists();
await click('[data-test-headlessui-disclosure-button="test-disclosure-btn-2"]');
assert.dom('[data-test-headlessui-disclosure-panel="disclosure-panel-2"]').exists();
await click('[data-test-headlessui-disclosure-button="test-disclosure-btn-2"]');
assert.dom('[data-test-headlessui-disclosure-panel="disclosure-panel-1"]').exists();
assert.dom('[data-test-headlessui-disclosure-panel="disclosure-panel-2"]').doesNotExist();
await click('[data-test-headlessui-disclosure-button="test-disclosure-btn-1"]');
assert.dom('[data-test-headlessui-disclosure-panel="disclosure-panel-1"]').doesNotExist();
assert.dom('[data-test-headlessui-disclosure-panel="disclosure-panel-2"]').doesNotExist();
});
});