Skip to content

Commit

Permalink
feat(picture): add picture component - FRONT-3868 (#2789)
Browse files Browse the repository at this point in the history
  • Loading branch information
emeryro committed Mar 6, 2023
1 parent 4718927 commit a95e781
Show file tree
Hide file tree
Showing 8 changed files with 393 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/implementations/twig/components/picture/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__snapshots__
*.js
63 changes: 63 additions & 0 deletions src/implementations/twig/components/picture/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# ECL Picture component

npm package: `@ecl/twig-component-picture`

```shell
npm install --save @ecl/twig-component-picture
```

## Parameters

- **"picture"** (associative array) (default: {}):
- **"img"** (associative array) (default: {}):
- "src" (string) (default: ''): Path to the image
- "alt" (string) (default: ''): Alt text of the image
- **"sources"** (array) (default: []): format: [
{
"src" (string) (default: ''): Path to the source image
"media" (string) (default: ''): Media condition to use this source. Could be a breakpoint ('s', 'm', 'l', 'xl') or a free string.
"type" (string) (default: ''): Type of this source
},
...
]
- **"extra_classes"** (optional) (string) (default: ''): Extra css classes, added to the root picture tag
- **"extra_image_classes"** (optional) (string) (default: ''): Extra css classes, added to to the img tag
- **"extra_attributes"** (optional) (array) (default: [])
- "name" (string) Attribute name, eg. 'data-test'
- "value" (optional) (string) Attribute value, eg: 'data-test-1'

## Example:

<!-- prettier-ignore -->
```twig
{% include '@ecl/picture/picture.html.twig' with {
picture: {
img: {
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image.jpg',
alt: 'Image alternative text',
},
sources: [
{
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image6.jpg',
media: '(min-width: 90rem)'
},
{
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image5.jpg',
media: 'xl',
},
{
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image4.jpg',
media: 'l',
},
{
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image3.jpg',
media: 'm',
},
{
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image2.jpg',
media: 's',
},
],
},
} %}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Picture Default renders correctly 1`] = `
<jest>
<picture
class="ecl-picture"
>
<source
media="(min-width: 90rem)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image6.jpg"
/>
<source
media="(min-width: 1140px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image5.jpg"
/>
<source
media="(min-width: 996px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image4.jpg"
/>
<source
media="(min-width: 768px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image3.jpg"
/>
<source
media="(min-width: 480px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image2.jpg"
/>
<img
alt="Image alternative text"
src="https://inno-ecl.s3.amazonaws.com/media/examples/example-image.jpg"
/>
</picture>
</jest>
`;

exports[`Picture Default renders correctly with extra attributes 1`] = `
<jest>
<picture
class="ecl-picture"
data-test="data-test-value"
data-test-1="data-test-value-1"
>
<source
media="(min-width: 90rem)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image6.jpg"
/>
<source
media="(min-width: 1140px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image5.jpg"
/>
<source
media="(min-width: 996px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image4.jpg"
/>
<source
media="(min-width: 768px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image3.jpg"
/>
<source
media="(min-width: 480px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image2.jpg"
/>
<img
alt="Image alternative text"
src="https://inno-ecl.s3.amazonaws.com/media/examples/example-image.jpg"
/>
</picture>
</jest>
`;

exports[`Picture Default renders correctly with extra class names 1`] = `
<jest>
<picture
class="ecl-picture custom-class custom-class--picture"
>
<source
media="(min-width: 90rem)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image6.jpg"
/>
<source
media="(min-width: 1140px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image5.jpg"
/>
<source
media="(min-width: 996px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image4.jpg"
/>
<source
media="(min-width: 768px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image3.jpg"
/>
<source
media="(min-width: 480px)"
srcset="https://inno-ecl.s3.amazonaws.com/media/examples/example-image2.jpg"
/>
<img
alt="Image alternative text"
class="custom-class custom-class--image"
src="https://inno-ecl.s3.amazonaws.com/media/examples/example-image.jpg"
/>
</picture>
</jest>
`;
27 changes: 27 additions & 0 deletions src/implementations/twig/components/picture/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@ecl/twig-component-picture",
"author": "European Commission",
"license": "EUPL-1.2",
"version": "3.7.1",
"description": "ECL Picture",
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@ecl/specs-component-picture": "3.7.1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ec-europa/europa-component-library.git"
},
"bugs": {
"url": "https://github.com/ec-europa/europa-component-library/issues"
},
"homepage": "https://github.com/ec-europa/europa-component-library",
"keywords": [
"ecl",
"europa-component-library",
"design-system",
"twig"
]
}
91 changes: 91 additions & 0 deletions src/implementations/twig/components/picture/picture.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{% apply spaceless %}

{#
Parameters:
- "picture" (associative array) (default: {}):
- "img" (associative array) (default: {}):
- "src" (string) (default: ''): Path to the image
- "alt" (string) (default: ''): Alt text of the image
- "sources" (array) (default: []): format: [
{
"src" (string) (default: ''): Path to the source image
"media" (string) (default: ''): Media condition to use this source. Could be a breakpoint ('s', 'm', 'l', 'xl') or a free string.
"type" (string) (default: ''): Type of this source
},
...
]
- "extra_classes" (optional) (string) (default: ''): Extra css classes, added to the root picture tag
- "extra_image_classes" (optional) (string) (default: ''): Extra css classes, added to to the img tag
- "extra_attributes" (optional) (array) (default: [])
- "name" (string) Attribute name, eg. 'data-test'
- "value" (optional) (string) Attribute value, eg: 'data-test-1'
#}

{# Internal properties #}

{% set _picture = picture|default({}) %}
{% set _extra_image_classes = extra_image_classes|default('') %}
{% set _css_class = 'ecl-picture' %}
{% set _extra_attributes = '' %}

{# Internal logic - Process properties #}

{% if extra_classes is defined and extra_classes is not empty %}
{% set _css_class = _css_class ~ ' ' ~ extra_classes %}
{% endif %}

{% if extra_attributes is defined and extra_attributes is not empty and extra_attributes is iterable %}
{% for attr in extra_attributes %}
{% if attr.value is defined %}
{% set _extra_attributes = _extra_attributes ~ ' ' ~ attr.name|e('html_attr') ~ '="' ~ attr.value|e('html_attr') ~ '"' %}
{% else %}
{% set _extra_attributes = _extra_attributes ~ ' ' ~ attr.name|e('html_attr') %}
{% endif %}
{% endfor %}
{% endif %}

{# Print the result #}

<picture class="{{ _css_class }}"{{ _extra_attributes|raw }}>
{% if _picture.sources is not empty and _picture.sources is iterable %}
{% for _source in _picture.sources %}
{% if _source.media == 's' %}
{% set _source = _source|merge({'media': '(min-width: 480px)'}) %}
{% elseif _source.media == 'm' %}
{% set _source = _source|merge({'media': '(min-width: 768px)'}) %}
{% elseif _source.media == 'l' %}
{% set _source = _source|merge({'media': '(min-width: 996px)'}) %}
{% elseif _source.media == 'xl' %}
{% set _source = _source|merge({'media': '(min-width: 1140px)'}) %}
{% endif %}

<source
{% if _source.src is not empty %}
srcset="{{ _source.src }}"
{% endif %}
{% if _source.media is not empty %}
media="{{ _source.media }}"
{% endif %}
{% if _source.type is not empty %}
type="{{ _source.type }}"
{% endif %}
>
{% endfor %}
{% endif %}

{% if _picture.img is not empty %}
<img
{% if _extra_image_classes is not empty %}
class="{{ _extra_image_classes }}"
{% endif %}
{% if _picture.img.src is not empty %}
src="{{ _picture.img.src }}"
{% endif %}
{% if _picture.img.alt is not empty %}
alt="{{ _picture.img.alt }}"
{% endif %}
/>
{% endif %}
</picture>

{% endapply %}
53 changes: 53 additions & 0 deletions src/implementations/twig/components/picture/picture.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
merge,
renderTwigFileAsNode,
renderTwigFileAsHtml,
} from '@ecl/test-utils';
import { axe, toHaveNoViolations } from 'jest-axe';

import data from '@ecl/specs-component-picture/demo/data';

expect.extend(toHaveNoViolations);

describe('Picture', () => {
const template = '@ecl/picture/picture.html.twig';
const render = (params) => renderTwigFileAsNode(template, params);

describe('Default', () => {
test('renders correctly', () => {
expect.assertions(1);

return expect(render(data)).resolves.toMatchSnapshot();
});

test('renders correctly with extra class names', () => {
expect.assertions(1);

const optionsWithExtraClasses = merge(data, {
extra_classes: 'custom-class custom-class--picture',
extra_image_classes: 'custom-class custom-class--image',
});

return expect(render(optionsWithExtraClasses)).resolves.toMatchSnapshot();
});

test('renders correctly with extra attributes', () => {
expect.assertions(1);

const withExtraAttributes = merge(data, {
extra_attributes: [
{ name: 'data-test', value: 'data-test-value' },
{ name: 'data-test-1', value: 'data-test-value-1' },
],
});

return expect(render(withExtraAttributes)).resolves.toMatchSnapshot();
});

test(`passes the accessibility tests`, async () => {
expect(
await axe(renderTwigFileAsHtml(template, data, true))
).toHaveNoViolations();
});
});
});
31 changes: 31 additions & 0 deletions src/specs/components/picture/demo/data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Simple content for demo
module.exports = {
picture: {
img: {
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image.jpg',
alt: 'Image alternative text',
},
sources: [
{
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image6.jpg',
media: '(min-width: 90rem)',
},
{
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image5.jpg',
media: 'xl',
},
{
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image4.jpg',
media: 'l',
},
{
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image3.jpg',
media: 'm',
},
{
src: 'https://inno-ecl.s3.amazonaws.com/media/examples/example-image2.jpg',
media: 's',
},
],
},
};
Loading

1 comment on commit a95e781

@github-actions
Copy link

Choose a reason for hiding this comment

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

Please sign in to comment.