diff --git a/src/elements/select/select.stories.ts b/src/elements/select/select.stories.ts
index 112d2be2a7..ff6dca943d 100644
--- a/src/elements/select/select.stories.ts
+++ b/src/elements/select/select.stories.ts
@@ -11,7 +11,7 @@ import type {
} from '@storybook/web-components';
import isChromatic from 'chromatic/isChromatic';
import type { TemplateResult } from 'lit';
-import { html, nothing } from 'lit';
+import { html } from 'lit';
import type { StyleInfo } from 'lit/directives/style-map.js';
import { styleMap } from 'lit/directives/style-map.js';
@@ -241,7 +241,7 @@ const textBlock = (text: string | null = null): TemplateResult => {
the form field, but it must always be covered by the select overlay.
`
- : nothing}
+ : text}
`;
};
diff --git a/src/elements/select/select.visual.spec.ts b/src/elements/select/select.visual.spec.ts
new file mode 100644
index 0000000000..88370f71dd
--- /dev/null
+++ b/src/elements/select/select.visual.spec.ts
@@ -0,0 +1,241 @@
+import { html, nothing, type TemplateResult } from 'lit';
+
+import { describeViewports, visualDiffDefault, visualDiffFocus } from '../core/testing/private.js';
+
+import '../form-error.js';
+import '../form-field.js';
+import '../option.js';
+import './select.js';
+
+describe('sbb-select', () => {
+ const valueEllipsis: string = 'This label name is so long that it needs ellipsis to fit.';
+ const defaultArgs = {
+ borderless: false,
+ negative: false,
+ disableOption: false,
+ withOptionGroup: false,
+ disableGroup: false,
+ withEllipsis: false,
+ value: undefined as string | string[] | undefined,
+ multiple: false,
+ disabled: false,
+ required: false,
+ readonly: false,
+ };
+
+ const createOptions = (
+ disableOption: boolean,
+ group: string | boolean,
+ selectValue: string | string[] | undefined = undefined,
+ ): TemplateResult[] => {
+ return new Array(5).fill(null).map((_, i) => {
+ const value = group ? `Option ${i + 1} ${' - ' + group}` : `Option ${i + 1}`;
+ const selected = Array.isArray(selectValue)
+ ? selectValue.includes(value)
+ : selectValue === value;
+ return html`
+
+ ${value}
+
+ `;
+ });
+ };
+
+ const createOptionsGroup = (
+ disableOption: boolean,
+ disableGroup: boolean,
+ ): TemplateResult => html`
+
+ ${createOptions(disableOption, '1')}
+
+ ${createOptions(disableOption, '2')}
+ `;
+
+ const template = ({
+ borderless,
+ negative,
+ disableOption,
+ withOptionGroup,
+ disableGroup,
+ withEllipsis,
+ ...args
+ }: typeof defaultArgs): TemplateResult => {
+ if (args.multiple && args.value) {
+ args.value = [args.value as string];
+ }
+ return html`
+
+
+
+ ${withEllipsis
+ ? html` ${valueEllipsis} `
+ : nothing}
+ ${withOptionGroup
+ ? createOptionsGroup(disableOption, disableGroup)
+ : createOptions(disableOption, false, args.value)}
+
+ ${args.required ? html`Error` : nothing}
+
+ `;
+ };
+
+ describeViewports({ viewports: ['zero', 'medium'], viewportHeight: 400 }, () => {
+ for (const negative of [false, true]) {
+ for (const visualDiffState of [visualDiffDefault, visualDiffFocus]) {
+ it(
+ `state=above negative=${negative} ${visualDiffState.name}`,
+ visualDiffState.with(async (setup) => {
+ await setup.withFixture(
+ html`
+
+ ${template({ ...defaultArgs, negative })}
+
+ `,
+ {
+ minHeight: '400px',
+ backgroundColor: negative ? 'var(--sbb-color-black)' : undefined,
+ },
+ );
+ setup.withPostSetupAction(() => {
+ const select = setup.snapshotElement.querySelector('sbb-select')!;
+ select.open();
+ });
+ }),
+ );
+ }
+ }
+ });
+
+ describeViewports({ viewports: ['zero', 'medium'] }, () => {
+ for (const negative of [false, true]) {
+ for (const visualDiffState of [visualDiffDefault, visualDiffFocus]) {
+ it(
+ `state=${visualDiffState.name} negative=${negative}`,
+ visualDiffState.with(async (setup) => {
+ await setup.withFixture(template({ ...defaultArgs, negative }), {
+ backgroundColor: negative ? 'var(--sbb-color-black)' : undefined,
+ });
+ }),
+ );
+ }
+
+ it(
+ `state=required negative=${negative}`,
+ visualDiffDefault.with(async (setup) => {
+ await setup.withFixture(template({ ...defaultArgs, negative, required: true }), {
+ backgroundColor: negative ? 'var(--sbb-color-black)' : undefined,
+ });
+ }),
+ );
+
+ it(
+ `state=disabled negative=${negative}`,
+ visualDiffDefault.with(async (setup) => {
+ await setup.withFixture(template({ ...defaultArgs, negative, disabled: true }), {
+ backgroundColor: negative ? 'var(--sbb-color-black)' : undefined,
+ });
+ }),
+ );
+
+ it(
+ `state=readonly negative=${negative}`,
+ visualDiffDefault.with(async (setup) => {
+ await setup.withFixture(template({ ...defaultArgs, negative, readonly: true }), {
+ backgroundColor: negative ? 'var(--sbb-color-black)' : undefined,
+ });
+ }),
+ );
+
+ it(
+ `state=borderless negative=${negative}`,
+ visualDiffDefault.with(async (setup) => {
+ await setup.withFixture(template({ ...defaultArgs, negative, borderless: true }), {
+ backgroundColor: negative ? 'var(--sbb-color-black)' : undefined,
+ });
+ }),
+ );
+
+ for (const multiple of [false, true]) {
+ it(
+ `negative=${negative} multiple=${multiple}`,
+ visualDiffDefault.with(async (setup) => {
+ await setup.withFixture(template({ ...defaultArgs, negative, multiple }), {
+ minHeight: '400px',
+ backgroundColor: negative ? 'var(--sbb-color-black)' : undefined,
+ });
+ setup.withPostSetupAction(() => {
+ const select = setup.snapshotElement.querySelector('sbb-select')!;
+ select.open();
+ });
+ }),
+ );
+
+ it(
+ `negative=${negative} multiple=${multiple} withEllipsis=true`,
+ visualDiffDefault.with(async (setup) => {
+ await setup.withFixture(
+ template({ ...defaultArgs, negative, multiple, withEllipsis: true }),
+ {
+ minHeight: '600px',
+ backgroundColor: negative ? 'var(--sbb-color-black)' : undefined,
+ },
+ );
+ setup.withPostSetupAction(() => {
+ const select = setup.snapshotElement.querySelector('sbb-select')!;
+ select.open();
+ });
+ }),
+ );
+
+ it(
+ `negative=${negative} multiple=${multiple} disableOption=true`,
+ visualDiffDefault.with(async (setup) => {
+ await setup.withFixture(
+ template({ ...defaultArgs, negative, multiple, disableOption: true }),
+ {
+ minHeight: '400px',
+ backgroundColor: negative ? 'var(--sbb-color-black)' : undefined,
+ },
+ );
+ setup.withPostSetupAction(() => {
+ const select = setup.snapshotElement.querySelector('sbb-select')!;
+ select.open();
+ });
+ }),
+ );
+
+ for (const disableGroup of [false, true]) {
+ it(
+ `negative=${negative} multiple=${multiple} withOptionGroup=true disableGroup=${disableGroup}`,
+ visualDiffDefault.with(async (setup) => {
+ await setup.withFixture(
+ template({
+ ...defaultArgs,
+ negative,
+ multiple,
+ disableGroup,
+ withOptionGroup: true,
+ }),
+ {
+ minHeight: '800px',
+ backgroundColor: negative ? 'var(--sbb-color-black)' : undefined,
+ },
+ );
+ setup.withPostSetupAction(() => {
+ const select = setup.snapshotElement.querySelector('sbb-select')!;
+ select.open();
+ });
+ }),
+ );
+ }
+ }
+ }
+ });
+});