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

amp-accordion #1849

Merged
merged 1 commit into from
Feb 12, 2016
Merged
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
67 changes: 67 additions & 0 deletions extensions/amp-accordion/0.1/amp-accordion.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright 2016 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* Non-overridable properties */
amp-accordion {
Copy link
Member

Choose a reason for hiding this comment

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

Does this FOUC?

It probably does. We may need to add this to the list of extensions that block doc render.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

Choose a reason for hiding this comment

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

This is more than just FOUC, right? This will cause a major re-layout when element is rebuilt. In particular, the overall height will change. Do we have solutions for this?

display: block !important;
}

/* Make sections non-floatable */
amp-accordion > section {
float: none !important;
}

/* Hide all children and make them non-floatable */
amp-accordion > section > *:nth-child(n) {
display: none !important;
float: none !important;
}

/* Display the first 2 elements (heading and content) */
amp-accordion > section > .-amp-accordion-header,
amp-accordion > section > .-amp-accordion-content {
display: block !important;
overflow: hidden !important; /* clearfix */
position: relative !important;
}

amp-accordion,
amp-accordion > section,
.-amp-accordion-header,
.-amp-accordion-content {
margin: 0;
}



/* heading element*/
.-amp-accordion-header {
cursor: pointer;
background-color: #efefef;
padding-right: 20px;
border: solid 1px #dfdfdf;
}

/* Collapse content by default. */
amp-accordion > section > .-amp-accordion-content {
display: none !important;
}

/* Expand content when needed. */
amp-accordion > section[expanded] > .-amp-accordion-content {
display: block !important;
}

76 changes: 76 additions & 0 deletions extensions/amp-accordion/0.1/amp-accordion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Copyright 2016 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {Layout} from '../../../src/layout';
import {assert} from '../../../src/asserts';
import {isExperimentOn} from '../../../src/experiments';
import {log} from '../../../src/log';

/** @const */
const EXPERIMENT = 'amp-accordion';

/** @const */
const TAG = 'AmpAccordion';

class AmpAccordion extends AMP.BaseElement {

/** @override */
isLayoutSupported(layout) {
return layout == Layout.CONTAINER;
}

/** @override */
buildCallback() {
/** @const @private {!NodeList} */
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add experiment as requested by @ericlindley-g

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

this.sections_ = this.getRealChildren();

/** @const @private {boolean} */
this.isExperimentOn_ = isExperimentOn(this.getWin(), EXPERIMENT);
if (!this.isExperimentOn_) {
log.warn(TAG, `Experiment ${EXPERIMENT} disabled`);
return;
}
this.sections_.forEach(section => {
assert(
section.tagName.toLowerCase() == 'section',
'Sections should be enclosed in a <section> tag, ' +
'See https://github.com/ampproject/amphtml/blob/master/extensions/' +
'amp-accordion/amp-accordion.md. Found in: %s', this.element);
const sectionComponents_ = section.children;
assert(
sectionComponents_.length == 2,
'Each section must have exactly two children. ' +
'See https://github.com/ampproject/amphtml/blob/master/extensions/' +
'amp-accordion/amp-accordion.md. Found in: %s', this.element);
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replied :)

const header = sectionComponents_[0];
const content = sectionComponents_[1];
header.classList.add('-amp-accordion-header');
content.classList.add('-amp-accordion-content');
header.addEventListener('click', event => {
event.preventDefault();
this.mutateElement(() => {
if (section.hasAttribute('expanded')) {
section.removeAttribute('expanded');
} else {
section.setAttribute('expanded', '');
}
}, content);
});
});
}
}

AMP.registerElement('amp-accordion', AmpAccordion, $CSS$);
90 changes: 90 additions & 0 deletions extensions/amp-accordion/0.1/test/test-amp-accordion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* Copyright 2016 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {Timer} from '../../../../src/timer';
import {adopt} from '../../../../src/runtime';
import {createIframePromise} from '../../../../testing/iframe';
import {toggleExperiment} from '../../../../src/experiments';
require('../../../../build/all/v0/amp-accordion-0.1.max');

adopt(window);

describe('amp-accordion', () => {
const timer = new Timer(window);
function getAmpAccordion() {
return createIframePromise().then(iframe => {
toggleExperiment(iframe.win, 'amp-accordion', true);
const ampAccordion = iframe.doc.createElement('amp-accordion');
for (let i = 0; i < 3; i++) {
const section = iframe.doc.createElement('section');
section.innerHTML = "<h2>Section " + i + "</h2><div>Loreum ipsum</div>";
ampAccordion.appendChild(section);
if (i == 1) {
section.setAttribute('expanded', '');
}
}
return iframe.addElement(ampAccordion).then(() => {
return Promise.resolve({
iframe: iframe,
ampAccordion: ampAccordion
});
});
});
}

it('should expand when header of a collapsed section is clicked', () => {
return getAmpAccordion().then(obj => {
const iframe = obj.iframe;
let clickEvent;
if (iframe.doc.createEvent) {
clickEvent = iframe.doc.createEvent('MouseEvent');
clickEvent.initMouseEvent('click', true, true, iframe.win, 1);
} else {
clickEvent = iframe.doc.createEventObject();
clickEvent.type = 'click';
}
const headerElements =
iframe.doc.querySelectorAll('section > *:first-child');
expect(headerElements[0].parentNode.hasAttribute('expanded')).to.be.false;
headerElements[0].dispatchEvent(clickEvent);
return timer.promise(50).then(() => {
expect(headerElements[0].parentNode.hasAttribute('expanded'))
.to.be.true;
});
});
});
it('should collapse when header of an expanded section is clicked', () => {
return getAmpAccordion().then(obj => {
const iframe = obj.iframe;
let clickEvent;
if (iframe.doc.createEvent) {
clickEvent = iframe.doc.createEvent('MouseEvent');
clickEvent.initMouseEvent('click', true, true, iframe.win, 1);
} else {
clickEvent = iframe.doc.createEventObject();
clickEvent.type = 'click';
}
const headerElements =
iframe.doc.querySelectorAll('section > *:first-child');
expect(headerElements[1].parentNode.hasAttribute('expanded')).to.be.true;
headerElements[1].dispatchEvent(clickEvent);
return timer.promise(50).then(() => {
expect(headerElements[1].parentNode.hasAttribute('expanded'))
.to.be.false;
});
});
});
});
64 changes: 64 additions & 0 deletions extensions/amp-accordion/amp-accordion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!---
Copyright 2016 The AMP HTML Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS-IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

### <a name="amp-accordion"></a> `amp-accordion`
Copy link
Contributor

Choose a reason for hiding this comment

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

Heads up that we're adding a new documentation structure on these pages. It might be nice to start using that here.

Check out https://github.com/ampproject/amphtml/pull/1841/files#diff-b06bcda88a7e36daa21769e39db62da6R17 for an example -- lines 17 to 36 with the header and the

are the key part.


An accordion provides a way for viewers to have a glance at the outline of the content and jump to a section or their choice at their will. This would be extremely helpful for handheld mobile devices where even a couple of sentences in a section would lead to the viewer needing to scroll.

#### Behavior
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add something about where the expand/collapse behavior is triggered? I assume it's the first child of a <section>, which is considered the heading, but it wasn't clear to me from this doc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


Each of the `amp-accordion` component’s immediate children is considered a section in the accordion. Each of these nodes must be a `<section>` tag.

- An `amp-accordion` can contain one or more `<section>`s as its direct children.
- Each `<section>` must contain only two direct children.
- The first child (of the section) will be considered as the heading of the section. Clicking/tapping on this section will trigger the expand/collapse behaviour.
- The second child (of the section) will be the content or the section
- There is no restriction on the type of tags that could be used for the `<section>`’s children.
- Any additional children of the `<section>` would be ignored not be displayed. (This should just be a safety backup and should be enforced in the validator)
- Clicking/tapping on the heading of a section expands/ or collapses the section.

```html
<amp-accordion>
<section expanded>
<h2>Section 1</h2>
<p>Bunch of awesome content</p>
</section>
<section>
<h2>Section 2</h2>
<div>Bunch of awesome content</div>
</section>
<section>
<h2>Section 3</h2>
<amp-img src="/awesome.png" width="300" height="300"></amp-img>
</section>
</amp-accordion>
```

#### Attributes

**expanded**

The `expanded` attribute can be set on any `<section>` that needs to be expanded on page load.

#### Styling

- You may use the `amp-accordion` element selector to style it freely.
- `amp-accordion` elements are always `display: block`.
- `<section>` and the heading and content element are not float-able.
- `<section>`s will have an `expanded` attribute when they are expanded.
- The content element is clear-fixed with `overflow: hidden` and hence cannot have scrollbars.
- margins of the `amp-accordion`, `<section>` and the heading and content elements are set to 0 and can be overridden in custom styles.
- Both the header and content elements are `position: relative`.
1 change: 1 addition & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ function buildExtensions(options) {
// and update it if any of its required deps changed.
// Each extension and version must be listed individually here.
buildExtension('amp-access', '0.1', true, options);
buildExtension('amp-accordion', '0.1', true, options);
buildExtension('amp-analytics', '0.1', false, options);
buildExtension('amp-anim', '0.1', false, options);
buildExtension('amp-audio', '0.1', false, options);
Expand Down
19 changes: 18 additions & 1 deletion src/base-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,6 @@ export class BaseElement {
* specified. Resource manager will perform the actual layout based on the
* priority of this element and its children.
* @param {!Element|!Array<!Element>} elements
* @param {boolean} inLocalViewport
* @protected
*/
scheduleLayout(elements) {
Expand Down Expand Up @@ -554,6 +553,24 @@ export class BaseElement {
this.resources_.attemptChangeHeight(this.element, newHeight, opt_callback);
}

/**
* Runs the specified mutation on the element and ensures that measures
* and layouts performed for the affected elements.
*
* This method should be called whenever a significant mutations are done
* on the DOM that could affect layout of elements inside this subtree or
* its siblings. The top-most affected element should be specified as the
* first argument to this method and all the mutation work should be done
* in the mutator callback which is called in the "mutation" vsync phase.
*
* @param {function()} mutator
* @param {Element=} opt_element
* @return {!Promise}
*/
mutateElement(mutator, opt_element) {
this.resources_.mutateElement(opt_element || this.element, mutator);
}

/**
* Schedules callback to be complete within the next batch. This call is
* intended for heavy DOM mutations that typically cause re-layouts.
Expand Down
1 change: 1 addition & 0 deletions src/render-delaying-extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {timer} from './timer';
* @const {!Array<string>}
*/
const EXTENSIONS = [
'amp-accordion',
'amp-dynamic-css-classes'
];

Expand Down
36 changes: 36 additions & 0 deletions test/manual/amp-accordion.amp.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!doctype html>
<html ⚡>
<head>
<meta charset="utf-8">
<title>AMP #0</title>
<link rel="canonical" href="amps.html" >
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<link href='https://fonts.googleapis.com/css?family=Questrial' rel='stylesheet' type='text/css'>
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<script async src="../../dist/amp.js"></script>
<script async custom-element="amp-youtube" src="../../dist/v0/amp-accordion-0.1.max.js"></script>
</head>
<body>
<h1>AMP #0</h1>
<amp-accordion>
<section>
<h2>Section 1</h2>
<div>
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus
</div>
</section>
<section>
<h2>Section 2</h2>
<div>
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus
</div>
</section>
<section>
<h2>Section 3</h2>
<div>
<amp-img id="img1" srcset="https://lh4.googleusercontent.com/kDqWeHQ0Zt-UFeHAlc2ydE9AwK7W-kbMXBqyuNvPNy0mNSGAP7FOVB3_1iAOdbQSsbZByErbehvvSKtnTb1L5GoYreijfKwgYwpP1eLCHyl9Am7BSpwRuBABOAO12PtyPBTirdAadnxOmfq9dH_rsLSTaYGmNz1D5QIwXeWd8UeDNUQ3f-cMgvkq4ePqmKoe9t5ySqqLMZs-v3wTi2pd4jV_CurzcMB76k_b4lyD5w77NUowkSaQfscYVQpkDjo6OgG2nBiKJ2TITWVDu2rvt6NuEoOD9xaHgXuv81OnOjXCokxZd5K6TZiAH-Qm1jTKGMANklXiXt6hbwrN3QPA1mq2FardxGNLj1_oqOpXaqfUuj8LvejFRY6zJMGq0r6S_TEtPvbyulIg4PkKPIaVzi5nVdGrAWWoesWh-ORqmxZ4FIhdbd_Igsdh5AcMETBcZz3l5-IX0hmnyUeT5IOPSGw-p3Esgp_abwWB9-kElEiiHPD4QuQ_swsRu0NSFwfRi_QefnJQJ5UATng6iVP3K0g7uumHcwLtFId0vCeHp4A=w1024-h768-no" width=512 height=344></amp-img>
</div>
</section>
</amp-accordion>
</body>
</html>
Loading