Skip to content

Commit

Permalink
feat(components): responsive images with CPicture
Browse files Browse the repository at this point in the history
closes #12
  • Loading branch information
LeBenLeBen committed Jan 19, 2021
1 parent ec527e1 commit 7a37dcb
Show file tree
Hide file tree
Showing 15 changed files with 265 additions and 0 deletions.
78 changes: 78 additions & 0 deletions packages/chusho/src/components/CPicture/CPicture.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { mount } from '@vue/test-utils';
import CPicture from './CPicture';

describe('CPicture', () => {
it('renders with the right attributes', () => {
const wrapper = mount(CPicture, {
props: {
src: '/image.jpg',
alt: 'alt',
},
});

expect(wrapper.html()).toBe(
'<picture><img src="/image.jpg" alt="alt"></picture>'
);
});

it('renders an empty alt by default', () => {
const wrapper = mount(CPicture, {
props: {
src: '/image.jpg',
},
});

expect(wrapper.html()).toBe(
'<picture><img src="/image.jpg" alt=""></picture>'
);
});

it('apply config class on img tag', () => {
const wrapper = mount(CPicture, {
global: {
provide: {
$chusho: {
options: {
components: {
picture: {
class: 'picture',
},
},
},
},
},
},
props: {
src: '/image.jpg',
class: 'special-picture',
},
});

expect(wrapper.find('img').classes()).toEqual([
'special-picture',
'picture',
]);
});

it('renders given sources', () => {
const wrapper = mount(CPicture, {
props: {
src: '/image.jpg',
sources: [
{
srcset: '[email protected] 2x, image.webp',
type: 'image/webp',
},
{
srcset: '[email protected] 2x, image.jpg',
type: 'image/jpeg',
},
],
},
});

expect(wrapper.html()).toBe(
'<picture><source srcset="[email protected] 2x, image.webp" type="image/webp"><source srcset="[email protected] 2x, image.jpg" type="image/jpeg"><img src="/image.jpg" alt=""></picture>'
);
});
});
57 changes: 57 additions & 0 deletions packages/chusho/src/components/CPicture/CPicture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
defineComponent,
h,
inject,
mergeProps,
PropType,
SourceHTMLAttributes,
} from 'vue';

import { DollarChusho } from '../../types';
import { generateConfigClass } from '../../utils/components';
import componentMixin from '../mixins/componentMixin';

export default defineComponent({
name: 'CPicture',

mixins: [componentMixin],

inheritAttrs: false,

props: {
/**
* Default/fallback image URL used in the `src` attribute.
*/
src: {
type: String,
required: true,
},
/**
* Alternative text description; leave empty if the image is not a key part of the content, otherwise describe what can be seen.
*/
alt: {
type: String,
default: '',
},
/**
* Generate multiple `source` elements with the given attributes.
*/
sources: {
type: Array as PropType<SourceHTMLAttributes[]>,
default: () => [],
},
},

render() {
const pictureConfig = inject<DollarChusho | null>('$chusho', null)?.options
?.components?.picture;
const elementProps: Record<string, unknown> = mergeProps(this.$attrs, {
src: this.$props.src,
alt: this.$props.alt,
...generateConfigClass(pictureConfig?.class, this.$props),
});
const sources = this.$props.sources.map((source) => h('source', source));

return h('picture', null, [...sources, h('img', elementProps)]);
},
});
3 changes: 3 additions & 0 deletions packages/chusho/src/components/CPicture/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import CPicture from './CPicture';

export { CPicture };
1 change: 1 addition & 0 deletions packages/chusho/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './CCollapse';
export * from './CTabs';
export * from './CDialog';
export * from './CAlert';
export * from './CPicture';
3 changes: 3 additions & 0 deletions packages/chusho/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ interface ComponentsOptions {
collapseContent?: ComponentCommonOptions & {
transition?: BaseTransitionProps;
};
picture?: {
class?: VueClassBinding | ClassGenerator;
};
}

export interface ChushoOptions {
Expand Down
1 change: 1 addition & 0 deletions packages/docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module.exports = {
'components/button.md',
'components/dialog.md',
'components/icon.md',
'components/picture.md',
'components/tabs.md',
'components/collapse.md',
],
Expand Down
56 changes: 56 additions & 0 deletions packages/docs/guide/components/picture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Picture

Easily generate responsive images.

## Config

### class

Classes applied to the component `img` element, except when the prop `bare` is set to `true`. See [styling components](/guide/styling-components/).

- **type:** `Array<String | Object> | Object | String | (props: Object) => {}`
- **default:** `null`

#### Example

```js
class: 'img-responsive'
```

## API

<Docgen :components="['CPicture']" />

## Examples

### Simplest

```vue
<CPicture src="/path/to/image.jpg" />
```

### With sources

```vue
<CPicture
:src="src"
:sources="[
{
srcset: '/path/to/[email protected] 2x, /path/to/image.webp',
type: 'image/webp',
},
{
srcset: '/path/to/[email protected] 2x, /path/to/image.jpg',
type: 'image/jpeg',
},
]"
/>
```

### With additional attributes

Attributes are not applied to the `picture` element but to the `img` element.

```vue
<CPicture src="/path/to/image.jpg" alt="Description" loading="lazy" />
```
3 changes: 3 additions & 0 deletions packages/playground/chusho.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export default {
height: 48,
class: 'inline-block align-middle pointer-events-none fill-current',
},
picture: {
class: 'picture',
},
tabs: {
class: 'tabs',
},
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<CPicture :src="src" class="img-responsive" />
</template>

<script>
import jpg from '../../../../assets/images/building.jpg';
export default {
data() {
return {
src: jpg,
};
},
};
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<template>
<CPicture
:sources="sources"
:src="src"
alt="Sky view from the middle of a C shaped building."
width="825"
height="550"
loading="lazy"
/>
</template>

<script>
import jpg from '../../../../assets/images/building.jpg';
import webp from '../../../../assets/images/building.webp';
import jpg2x from '../../../../assets/images/[email protected]';
import webp2x from '../../../../assets/images/[email protected]';
export default {
data() {
return {
src: jpg,
sources: [
{
srcset: `${webp2x} 2x, ${webp}`,
type: 'image/webp',
},
{
srcset: `${jpg2x} 2x, ${jpg}`,
type: 'image/jpeg',
},
],
};
},
};
</script>
13 changes: 13 additions & 0 deletions packages/playground/src/components/examples/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,19 @@
"component": "Controlled"
}
]
},
"picture": {
"label": "Picture",
"variants": [
{
"label": "Default",
"component": "Default"
},
{
"label": "With Sources",
"component": "WithSources"
}
]
}
}
},
Expand Down

0 comments on commit 7a37dcb

Please sign in to comment.