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

feat(sbb-container): support for background image #2999

Merged
merged 6 commits into from
Aug 15, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ snapshots["sbb-container renders DOM"] =

snapshots["sbb-container renders Shadow DOM"] =
`<div class="sbb-container">
<slot>
<slot name="image">
</slot>
<div class="sbb-container__content">
<slot>
</slot>
</div>
</div>
<slot name="sticky-bar">
</slot>
Expand Down
35 changes: 35 additions & 0 deletions src/elements/container/container/container.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@
--sbb-container-background-color: var(--sbb-color-milk);
}

:host([data-slot-names~='image']) {
--sbb-container-background-color: transparent;
--sbb-container-padding: var(--sbb-spacing-responsive-xxl);
}

:host(:not([expanded])[background-expanded]) {
background-color: var(--sbb-container-background-color);
}

.sbb-container {
background-color: var(--sbb-container-background-color);
padding: var(--sbb-container-padding);

@include sbb.ignore-children-margin;

Expand All @@ -38,4 +44,33 @@
:host([expanded]) & {
@include sbb.page-spacing-expanded;
}

:host([data-slot-names~='image']) & {
position: relative;
}
}

.sbb-container__content {
position: relative;
}

::slotted([slot='image']) {
--sbb-image-border-radius: 0;

position: absolute;
inset: 0;

:host(:not([expanded])) & {
@include sbb.mq($from: ultra) {
--sbb-image-border-radius: var(--sbb-border-radius-4x);

border-radius: var(--sbb-border-radius-4x);
}
}
}

::slotted(img[slot='image']) {
object-fit: cover;
height: 100%;
width: 100%;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe(`sbb-container`, () => {
let element: SbbContainerElement;

beforeEach(async () => {
element = await fixture(html` <sbb-container></sbb-container> `);
element = await fixture(html`<sbb-container></sbb-container>`);
});

it('DOM', async () => {
Expand Down
70 changes: 65 additions & 5 deletions src/elements/container/container/container.stories.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import type { InputType } from '@storybook/types';
import type { ArgTypes, Args, Meta, StoryObj } from '@storybook/web-components';
import { type TemplateResult, html, nothing } from 'lit';
import type { Args, ArgTypes, Meta, StoryObj } from '@storybook/web-components';
import { html, nothing, type TemplateResult } from 'lit';

import { sbbSpread } from '../../../storybook/helpers/spread.js';
import sampleImages from '../../core/images.js';

import '../../button/secondary-button.js';
import '../../card.js';
import '../../image.js';
import '../../title.js';
import './container.js';

Expand All @@ -19,9 +22,22 @@ const containerContent = (title: string, last = false): TemplateResult => html`
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
voluptate velit esse cillum dolore eu fugiat nulla pariatur.
</p>
<sbb-secondary-button style=${last ? 'margin-block-end: 3rem;' : nothing}
>See more</sbb-secondary-button
>
<sbb-secondary-button style=${last ? 'margin-block-end: 3rem;' : nothing}>
See more
</sbb-secondary-button>
`;

const card = (title: string): TemplateResult => html`
<sbb-card size="xxl">
<sbb-title level="5">${title}</sbb-title>
<p class="sbb-text-s" style="margin: 0">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua.
</p>
<sbb-secondary-button style="margin-block-start: var(--sbb-spacing-responsive-xs)">
See more
</sbb-secondary-button>
</sbb-card>
`;

const color: InputType = {
Expand All @@ -43,16 +59,24 @@ const backgroundExpanded: InputType = {
},
};

const imageSrc: InputType = {
control: {
type: 'text',
},
};

const defaultArgTypes: ArgTypes = {
color,
expanded,
'background-expanded': backgroundExpanded,
'image-src': imageSrc,
};

const defaultArgs: Args = {
color: color.options![0],
expanded: false,
'background-expanded': false,
'image-src': undefined,
};

const DefaultTemplate = (args: Args): TemplateResult => html`
Expand All @@ -62,6 +86,33 @@ const DefaultTemplate = (args: Args): TemplateResult => html`
</sbb-container>
`;

const BackgroundImageTemplate = ({ 'image-src': imageSrc, ...args }: Args): TemplateResult => html`
<sbb-container ${sbbSpread(args)}>
<sbb-title level="2" style="margin: 0">Container with background image</sbb-title>
<style>
.content {
display: flex;
margin-block-start: var(--sbb-spacing-responsive-m);
gap: var(--sbb-spacing-fixed-6x);
justify-content: center;
flex-direction: column;

/* Starting from breakpoint medium. Please use design token. */
@media screen and (width >= 840px) {
flex-direction: row;
}
}
</style>
<div class="content">${card('Example title')} ${card('Another one')}</div>
<sbb-image
slot="image"
image-src=${imageSrc}
alt="Train"
style="--sbb-image-object-position: bottom;"
></sbb-image>
</sbb-container>
`;

// Only for visual regression
const NestedContainerTemplate = (): TemplateResult => html`
<sbb-container color="white">
Expand Down Expand Up @@ -92,6 +143,15 @@ export const Milk: StoryObj = {
args: { ...defaultArgs, color: color.options![2] },
};

export const BackgroundImage: StoryObj = {
render: BackgroundImageTemplate,
argTypes: defaultArgTypes,
args: {
...defaultArgs,
'image-src': sampleImages[10],
},
};

export const Expanded: StoryObj = {
render: DefaultTemplate,
argTypes: defaultArgTypes,
Expand Down
9 changes: 8 additions & 1 deletion src/elements/container/container/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ import {
} from 'lit';
import { customElement, property } from 'lit/decorators.js';

import { slotState } from '../../core/decorators.js';

import style from './container.scss?lit&inline';

/**
* It displays its content with the default page spacing.
*
* @slot - Use the unnamed slot to add anything to the container.
* @slot sticky-bar - The slot used by the sbb-sticky-bar component.
* @slot image - The slot used to slot an `sbb-image` to use as background.
*/
@customElement('sbb-container')
@slotState()
export class SbbContainerElement extends LitElement {
public static override styles: CSSResultGroup = style;

Expand All @@ -40,7 +44,10 @@ export class SbbContainerElement extends LitElement {
protected override render(): TemplateResult {
return html`
<div class="sbb-container">
<slot></slot>
<slot name="image"></slot>
<div class="sbb-container__content">
<slot></slot>
</div>
</div>
<slot name="sticky-bar"></slot>
`;
Expand Down
60 changes: 57 additions & 3 deletions src/elements/container/container/container.visual.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,42 @@ import { SbbBreakpointUltraMin } from '@sbb-esta/lyne-design-tokens';
import { setViewport } from '@web/test-runner-commands';
import { html, type TemplateResult } from 'lit';

import { describeViewports, visualDiffDefault } from '../../core/testing/private.js';
import {
describeViewports,
loadAssetAsBase64,
visualDiffDefault,
} from '../../core/testing/private.js';
import { waitForImageReady } from '../../core/testing.js';

import './container.js';
import '../../title.js';
import '../../button.js';
import '../../card.js';
import '../../image.js';
import '../../title.js';
import './container.js';

const imageUrl = import.meta.resolve('../../core/testing/assets/placeholder-image.png');
const imageBase64 = await loadAssetAsBase64(imageUrl);

describe(`sbb-container`, () => {
const colorCases = ['transparent', 'white', 'milk'];

const backgroundExpandedCases = [false, true];

const images = [
{
selector: 'sbb-image',
image: html`<sbb-image slot="image" image-src=${imageUrl}></sbb-image>`,
},
{
selector: 'img',
image: html`<img
slot="image"
src=${imageBase64}
alt=''
></img>`,
},
];

const containerContent = (): TemplateResult => html`
<sbb-title level="4">Example title</sbb-title>
<p class="sbb-text-s">The container component will give its content the correct spacing.</p>
Expand All @@ -25,6 +50,14 @@ describe(`sbb-container`, () => {
<sbb-secondary-button style="margin-block-end: 3rem;">See more</sbb-secondary-button>
`;

const backgroundImageContent = html`
<sbb-title level="2" style="margin-block-start: 0">Container with background image</sbb-title>
<sbb-card size="xxl">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua.
</sbb-card>
`;

const wrapperStyles = { backgroundColor: 'var(--sbb-color-silver)', padding: '0' };

describeViewports({ viewportHeight: 600 }, () => {
Expand All @@ -49,6 +82,27 @@ describe(`sbb-container`, () => {
);
}),
);

describe('background-image', () => {
for (const expanded of [false, true]) {
describe(`expanded=${expanded}`, () => {
for (const image of images) {
it(
`slotted=${image.selector}`,
visualDiffDefault.with(async (setup) => {
await setup.withFixture(
html`<sbb-container ?expanded=${expanded}>
${backgroundImageContent} ${image.image}
</sbb-container>`,
);

await waitForImageReady(setup.snapshotElement.querySelector(image.selector)!);
}),
);
}
});
}
});
});

describeViewports({ viewports: ['medium'] }, () => {
Expand Down
16 changes: 11 additions & 5 deletions src/elements/container/container/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ The `sbb-container` is a component that displays its content with the default pa

The `sbb-container` content is provided via an unnamed slot.

The `image` slot can be used to place a background image. If you need to control the object position,
use CSS object-position for slotted `img`, or `--sbb-image-object-position` variable for slotted `sbb-image`.
If an image is present, the container receives a pre-defined padding.
It's possible to override the padding by using the CSS variable `--sbb-container-padding`.

## Style

By default `sbb-container` uses the `page spacing` defined in the [layout documentation](/docs/styles-layout--docs). Optionally the user can use the `expanded` property (default: `false`) to switch to the `page spacing expanded` layout.
Spacing options are applied to all of the container's content, including the `sbb-sticky-bar`.
Spacing options are applied to all the container's content, including the `sbb-sticky-bar`.
The component has also four color variants that can be set using the `color` property (default: `white`).

```html
Expand All @@ -36,7 +41,8 @@ The component has also four color variants that can be set using the `color` pro

## Slots

| Name | Description |
| ------------ | ------------------------------------------------------ |
| | Use the unnamed slot to add anything to the container. |
| `sticky-bar` | The slot used by the sbb-sticky-bar component. |
| Name | Description |
| ------------ | ---------------------------------------------------------- |
| | Use the unnamed slot to add anything to the container. |
| `image` | The slot used to slot an `sbb-image` to use as background. |
| `sticky-bar` | The slot used by the sbb-sticky-bar component. |
3 changes: 2 additions & 1 deletion src/elements/core/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const sampleImages: string[] = [
'https://cdn.img.sbb.ch/content/dam/internet/lyne/Bahnhof-Luzern.jpg',
'https://cdn.img.sbb.ch/content/dam/internet/lyne/Einheitswagen-IV-EuroCity.jpg',
'https://cdn.img.sbb.ch/content/dam/internet/lyne/Einsatzstrecken_EW4-Eurocity.jpg',
'https://cdn.img.sbb.ch/content/dam/internet/sharedimages/grafiken/Frau-No-Results.png',
'https://cdn.img.sbb.ch/content/dam/internet/lyne/Frau-No-Results.png',
'https://cdn.img.sbb.ch/content/dam/internet/lyne/Help-Teaser-Landscape.png',
];

export default sampleImages;
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ snapshots["sbb-overlay renders Shadow DOM"] =
<sbb-container
class="sbb-overlay__content-container"
color="transparent"
data-slot-names="unnamed"
>
<slot>
</slot>
Expand Down
Loading