Skip to content

Commit

Permalink
Merge pull request ampproject#375 from dvoytenko/fixed-height
Browse files Browse the repository at this point in the history
New layout: fixed-height
  • Loading branch information
dvoytenko committed Oct 1, 2015
2 parents f83c51f + db3925d commit 5a2a083
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 25 deletions.
5 changes: 5 additions & 0 deletions css/amp.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ body {
position: relative;
}

.-amp-layout-fixed-height {
display: block;
position: relative;
}

.-amp-layout-container {
display: block;
position: relative;
Expand Down
2 changes: 1 addition & 1 deletion examples/everything.amp.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ <h1>AMP #0</h1>
</amp-carousel>
</p>
<p>
<amp-carousel width=400 height=200>
<amp-carousel width=auto height=200>
<amp-img src="https://lh3.googleusercontent.com/pSECrJ82R7-AqeBCOEPGPM9iG9OEIQ_QXcbubWIOdkY=w300-h200-no" width=300 height=200></amp-img>
<amp-img src="https://lh3.googleusercontent.com/5rcQ32ml8E5ONp9f9-Rf78IofLb9QjS5_0mqsY1zEFc=w300-h200-no" width=300 height=200></amp-img>
<amp-img src="https://lh3.googleusercontent.com/Z4gtm5Bkxyv21Z2PtbTf95Clb9AE4VTR6olbBKYrenM=w300-h200-no" width=300 height=200></amp-img>
Expand Down
2 changes: 1 addition & 1 deletion extensions/amp-audio/0.1/amp-audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class AmpAudio extends AMP.BaseElement {

/** @override */
isLayoutSupported(layout) {
return layout === Layout.FIXED;
return layout == Layout.FIXED || layout == Layout.FIXED_HEIGHT;
}


Expand Down
2 changes: 1 addition & 1 deletion extensions/amp-carousel/0.1/carousel.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class AmpCarousel extends BaseCarousel {

/** @override */
isLayoutSupported(layout) {
return layout == Layout.FIXED;
return layout == Layout.FIXED || layout == Layout.FIXED_HEIGHT;
}

/** @override */
Expand Down
2 changes: 1 addition & 1 deletion extensions/amp-fit-text/amp-fit-text.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ the minimum font size, the overflowing content will be cut off and hidden. The
WebKit and Blink-based browsers will show ellipsis in this case.

The `amp-fit-text` accepts one of the following `layout` values: `fixed`,
`responsive` or `fill`.
`fixed-height`, `responsive` or `fill`.

For example:

Expand Down
5 changes: 3 additions & 2 deletions spec/amp-html-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,9 @@ Depending on the value of the `layout` attribute AMP component elements must hav

The optional layout attribute allows specifying how the component behaves in the document layout. Valid values for the layout attribute are:

- Not present: If `width` and `height` attributes are present `fixed` layout is assumed. If `width` and `height` are not present `container` layout is assumed (unless otherwise documented with the component) which may not be supported by the element (Would trigger a runtime error).
- fixed:
- Not present: If `width` equals to `auto` `fixed-height` layout is assumed. If `width` or `height` attributes are present `fixed` layout is assumed. If `width` and `height` are not present `container` layout is assumed (unless otherwise documented with the component) which may not be supported by the element (Would trigger a runtime error).
- `fixed`: The `width` and `height` attributes must be present. The only exceptions are `amp-pixel` and `amp-audio` elements.
- `fixed-height`: The `height` attribute must be present. The `width` attribute must not be present or must be equal to `auto`.
- `responsive`: The `width` and `height` attributes must be present and are used to determine the aspect ratio of the component and the component is sized to the width of its container element while maintaining the height based on the aspect ratio.
- `nodisplay`: The component takes up zero space on the screen as if its display style was `none`. The `width` and `height` attributes are not required.
- `fill`: Element size will be determined by the parent element.
Expand Down
36 changes: 28 additions & 8 deletions src/custom-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,13 @@ export function applyLayout_(element) {

// Handle elements that do not specify a width/height and are defined to have
// natural browser dimensions.
if ((!layoutAttr || layoutAttr === Layout.FIXED) &&
if ((!layoutAttr || layoutAttr == Layout.FIXED ||
layoutAttr == Layout.FIXED_HEIGHT) &&
(!widthAttr || !heightAttr) && hasNaturalDimensions(element.tagName)) {
let dimensions = getNaturalDimensions(element.tagName);
widthAttr = widthAttr || dimensions.width;
if (layoutAttr != Layout.FIXED_HEIGHT) {
widthAttr = widthAttr || dimensions.width;
}
heightAttr = heightAttr || dimensions.height;
}

Expand All @@ -112,19 +115,34 @@ export function applyLayout_(element) {
if (!layout) {
throw new Error('Unknown layout: ' + layoutAttr);
}
} else if (widthAttr || heightAttr) {
if (!widthAttr || widthAttr == 'auto') {
layout = Layout.FIXED_HEIGHT;
} else {
layout = Layout.FIXED;
}
} else {
layout = (widthAttr || heightAttr) ? Layout.FIXED : Layout.CONTAINER;
layout = Layout.CONTAINER;
}
element.classList.add(getLayoutClass(layout));
if (isLayoutSizeDefined(layout)) {
element.classList.add('-amp-layout-size-defined');
}

if (layout == Layout.FIXED || layout == Layout.RESPONSIVE) {
let width = parseLength(widthAttr);
if (!width) {
throw new Error('Expected width to be available and be an ' +
'integer/length value: ' + widthAttr);
if (layout == Layout.FIXED || layout == Layout.FIXED_HEIGHT ||
layout == Layout.RESPONSIVE) {
let width = 0;
if (layout == Layout.FIXED_HEIGHT) {
if (widthAttr && widthAttr != 'auto') {
throw new Error('Expected width to be either absent or equal "auto" ' +
'for fixed-height layout: ' + widthAttr);
}
} else {
width = parseLength(widthAttr);
if (!width) {
throw new Error('Expected width to be available and be an ' +
'integer/length value: ' + widthAttr);
}
}
let height = parseLength(heightAttr);
if (!height) {
Expand All @@ -141,6 +159,8 @@ export function applyLayout_(element) {
sizer.style.paddingTop =
((getLengthNumeral(height) / getLengthNumeral(width)) * 100) + '%';
element.insertBefore(sizer, element.firstChild);
} else if (layout == Layout.FIXED_HEIGHT) {
element.style.height = height;
} else {
element.style.width = width;
element.style.height = height;
Expand Down
2 changes: 2 additions & 0 deletions src/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {assert} from './asserts';
export const Layout = {
NODISPLAY: 'nodisplay',
FIXED: 'fixed',
FIXED_HEIGHT: 'fixed-height',
RESPONSIVE: 'responsive',
CONTAINER: 'container',
FILL: 'fill'
Expand Down Expand Up @@ -65,6 +66,7 @@ export function getLayoutClass(layout) {
*/
export function isLayoutSizeDefined(layout) {
return (layout == Layout.FIXED ||
layout == Layout.FIXED_HEIGHT ||
layout == Layout.RESPONSIVE ||
layout == Layout.FILL);
}
Expand Down
116 changes: 106 additions & 10 deletions test/functional/test-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe('Layout', () => {
it('parseLayout', () => {
expect(parseLayout('nodisplay')).to.equal('nodisplay');
expect(parseLayout('fixed')).to.equal('fixed');
expect(parseLayout('fixed-height')).to.equal('fixed-height');
expect(parseLayout('responsive')).to.equal('responsive');
expect(parseLayout('container')).to.equal('container');
expect(parseLayout('fill')).to.equal('fill');
Expand Down Expand Up @@ -69,20 +70,21 @@ describe('Layout', () => {
expect(div.style.width).to.equal('');
expect(div.style.height).to.equal('');
expect(div.style.display).to.equal('none');
expect(div.classList.contains('-amp-layout-nodisplay')).to.equal(true);
expect(div.classList.contains('-amp-layout-size-defined')).to.equal(false);
expect(div).to.have.class('-amp-layout-nodisplay');
expect(div).to.not.have.class('-amp-layout-size-defined');
expect(div.children.length).to.equal(0);
});


it('layout=fixed', () => {
div.setAttribute('layout', 'fixed');
div.setAttribute('width', 100);
div.setAttribute('height', 200);
expect(applyLayout_(div)).to.equal(Layout.FIXED);
expect(div.style.width).to.equal('100px');
expect(div.style.height).to.equal('200px');
expect(div.classList.contains('-amp-layout-fixed')).to.equal(true);
expect(div.classList.contains('-amp-layout-size-defined')).to.equal(true);
expect(div).to.have.class('-amp-layout-fixed');
expect(div).to.have.class('-amp-layout-size-defined');
expect(div.children.length).to.equal(0);
});

Expand All @@ -100,15 +102,70 @@ describe('Layout', () => {
/to be available and be an integer/);
});


it('layout=fixed-height', () => {
div.setAttribute('layout', 'fixed-height');
div.setAttribute('height', 200);
expect(applyLayout_(div)).to.equal(Layout.FIXED_HEIGHT);
expect(div.style.width).to.equal('');
expect(div.style.height).to.equal('200px');
expect(div).to.have.class('-amp-layout-fixed-height');
expect(div).to.have.class('-amp-layout-size-defined');
expect(div.children.length).to.equal(0);
});

it('layout=fixed-height, with width=auto', () => {
div.setAttribute('layout', 'fixed-height');
div.setAttribute('height', 200);
div.setAttribute('width', 'auto');
expect(applyLayout_(div)).to.equal(Layout.FIXED_HEIGHT);
expect(div.style.width).to.equal('');
expect(div.style.height).to.equal('200px');
expect(div).to.have.class('-amp-layout-fixed-height');
expect(div).to.have.class('-amp-layout-size-defined');
expect(div.children.length).to.equal(0);
});

it('layout=fixed-height, prohibit width!=auto', () => {
div.setAttribute('layout', 'fixed-height');
div.setAttribute('height', 200);
div.setAttribute('width', 300);
expect(function() {
applyLayout_(div);
}).to.throw(/Expected width to be either absent or equal "auto"/);
});

it('layout=fixed-height - default with height', () => {
div.setAttribute('height', 200);
expect(applyLayout_(div)).to.equal(Layout.FIXED_HEIGHT);
expect(div.style.height).to.equal('200px');
expect(div.style.width).to.equal('');
});

it('layout=fixed-height - default with height and width=auto', () => {
div.setAttribute('height', 200);
div.setAttribute('width', 'auto');
expect(applyLayout_(div)).to.equal(Layout.FIXED_HEIGHT);
expect(div.style.height).to.equal('200px');
expect(div.style.width).to.equal('');
});

it('layout=fixed-height - requires height', () => {
div.setAttribute('layout', 'fixed-height');
expect(() => applyLayout_(div)).to.throw(
/to be available and be an integer/);
});


it('layout=responsive', () => {
div.setAttribute('layout', 'responsive');
div.setAttribute('width', 100);
div.setAttribute('height', 200);
expect(applyLayout_(div)).to.equal(Layout.RESPONSIVE);
expect(div.style.width).to.equal('');
expect(div.style.height).to.equal('');
expect(div.classList.contains('-amp-layout-responsive')).to.equal(true);
expect(div.classList.contains('-amp-layout-size-defined')).to.equal(true);
expect(div).to.have.class('-amp-layout-responsive');
expect(div).to.have.class('-amp-layout-size-defined');
expect(div.children.length).to.equal(1);
expect(div.children[0].tagName.toLowerCase()).to.equal('i-amp-sizer');
expect(div.children[0].style.paddingTop).to.equal('200%');
Expand All @@ -119,8 +176,8 @@ describe('Layout', () => {
expect(applyLayout_(div)).to.equal(Layout.FILL);
expect(div.style.width).to.equal('');
expect(div.style.height).to.equal('');
expect(div.classList.contains('-amp-layout-fill')).to.equal(true);
expect(div.classList.contains('-amp-layout-size-defined')).to.equal(true);
expect(div).to.have.class('-amp-layout-fill');
expect(div).to.have.class('-amp-layout-size-defined');
expect(div.children.length).to.equal(0);
});

Expand All @@ -129,8 +186,8 @@ describe('Layout', () => {
expect(applyLayout_(div)).to.equal(Layout.CONTAINER);
expect(div.style.width).to.equal('');
expect(div.style.height).to.equal('');
expect(div.classList.contains('-amp-layout-container')).to.equal(true);
expect(div.classList.contains('-amp-layout-size-defined')).to.equal(false);
expect(div).to.have.class('-amp-layout-container');
expect(div).to.not.have.class('-amp-layout-size-defined');
expect(div.children.length).to.equal(0);
});

Expand All @@ -141,4 +198,43 @@ describe('Layout', () => {
}).to.throw(/Unknown layout: foo/);
});


it('should configure natural dimensions; default layout', () => {
let pixel = document.createElement('amp-pixel');
expect(applyLayout_(pixel)).to.equal(Layout.FIXED);
expect(pixel.style.width).to.equal('1px');
expect(pixel.style.height).to.equal('1px');
});

it('should configure natural dimensions; default layout; with width', () => {
let pixel = document.createElement('amp-pixel');
pixel.setAttribute('width', '11');
expect(applyLayout_(pixel)).to.equal(Layout.FIXED);
expect(pixel.style.width).to.equal('11px');
expect(pixel.style.height).to.equal('1px');
});

it('should configure natural dimensions; default layout; with height', () => {
let pixel = document.createElement('amp-pixel');
pixel.setAttribute('height', '11');
expect(applyLayout_(pixel)).to.equal(Layout.FIXED);
expect(pixel.style.width).to.equal('1px');
expect(pixel.style.height).to.equal('11px');
});

it('should configure natural dimensions; layout=fixed', () => {
let pixel = document.createElement('amp-pixel');
pixel.setAttribute('layout', 'fixed');
expect(applyLayout_(pixel)).to.equal(Layout.FIXED);
expect(pixel.style.width).to.equal('1px');
expect(pixel.style.height).to.equal('1px');
});

it('should configure natural dimensions; layout=fixed-height', () => {
let pixel = document.createElement('amp-pixel');
pixel.setAttribute('layout', 'fixed-height');
expect(applyLayout_(pixel)).to.equal(Layout.FIXED_HEIGHT);
expect(pixel.style.height).to.equal('1px');
expect(pixel.style.width).to.equal('');
});
});
2 changes: 1 addition & 1 deletion test/manual/amp-carousel.amp.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<body>
<h1>amp #0</h1>

<amp-carousel id="carousel-1" width=400 height=200>
<amp-carousel id="carousel-1" height=200 layout=fixed-height>
<amp-img src="https://lh3.googleusercontent.com/pSECrJ82R7-AqeBCOEPGPM9iG9OEIQ_QXcbubWIOdkY=w300-h200-no" width=300 height=200></amp-img>
<amp-img src="https://lh3.googleusercontent.com/5rcQ32ml8E5ONp9f9-Rf78IofLb9QjS5_0mqsY1zEFc=w300-h200-no" width=300 height=200></amp-img>
<amp-img src="https://lh3.googleusercontent.com/Z4gtm5Bkxyv21Z2PtbTf95Clb9AE4VTR6olbBKYrenM=w300-h200-no" width=300 height=200></amp-img>
Expand Down

0 comments on commit 5a2a083

Please sign in to comment.