Skip to content

Commit 052b02c

Browse files
authored
experiment: add details base styles and visual tests (#9165)
1 parent 005d360 commit 052b02c

18 files changed

+269
-48
lines changed

.github/workflows/visual-tests.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,42 @@ permissions:
66
contents: read
77

88
jobs:
9+
base:
10+
name: Base
11+
runs-on: ubuntu-22.04
12+
if: github.repository_owner == 'vaadin'
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
with:
17+
fetch-depth: '0'
18+
19+
- name: Setup Node
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: '22'
23+
cache: 'yarn'
24+
25+
- name: Install Dependencies
26+
run: yarn --frozen-lockfile --no-progress --non-interactive
27+
28+
- name: Visual tests
29+
uses: nick-fields/retry@v3
30+
env:
31+
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
32+
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
33+
with:
34+
timeout_minutes: 20
35+
retry_wait_seconds: 60
36+
max_attempts: 3
37+
command: yarn test:base
38+
39+
- uses: actions/upload-artifact@v4
40+
if: ${{ failure() }}
41+
with:
42+
name: screenshots
43+
path: |
44+
packages/*/test/visual/base/screenshots/*/failed/*.png
945
lumo:
1046
name: Lumo
1147
runs-on: ubuntu-latest

dev/details.html

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
@@ -9,23 +9,24 @@
99

1010
<script type="module">
1111
import '@vaadin/details';
12-
import '@vaadin/tooltip';
13-
// import '@vaadin/details/theme/material/vaadin-details.js';
14-
// import '@vaadin/tooltip/theme/material/vaadin-tooltip.js';
1512
</script>
1613
</head>
1714

1815
<body>
19-
<vaadin-details summary="Expandable Details">
20-
<div>Toggle using mouse, Enter and Space keys.</div>
16+
<vaadin-details summary="Details 1" opened>
17+
<div>Details 1 content.</div>
2118
</vaadin-details>
2219

23-
<!--
24-
<vaadin-details>
25-
<vaadin-details-summary slot="summary">Expandable Details</vaadin-details-summary>
26-
<div>Toggle using mouse, Enter and Space keys.</div>
27-
<vaadin-tooltip slot="tooltip" text="Click to toggle"></vaadin-tooltip>
20+
<vaadin-details summary="Details 2">
21+
<div>Details 2 content.</div>
22+
</vaadin-details>
23+
24+
<vaadin-details summary="Details 3 (disabled)" disabled>
25+
<div>Details 3 content.</div>
26+
</vaadin-details>
27+
28+
<vaadin-details summary="Details 4 (disabled)" disabled opened>
29+
<div>Details 4 content.</div>
2830
</vaadin-details>
29-
-->
3031
</body>
3132
</html>

packages/details/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
"type": "module",
2222
"files": [
2323
"src",
24+
"!src/vaadin-details-summary-base-styles.d.ts",
25+
"!src/vaadin-details-summary-base-styles.js",
2426
"theme",
2527
"vaadin-*.d.ts",
2628
"vaadin-*.js",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2025 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import type { CSSResult } from 'lit';
7+
8+
export const detailsSummary: (partName?: string) => CSSResult;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2025 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import '@vaadin/component-base/src/style-props.js';
7+
import { css, unsafeCSS } from 'lit';
8+
9+
export const detailsSummary = (partName = 'vaadin-details-summary') => css`
10+
:host {
11+
align-items: center;
12+
background: var(--${unsafeCSS(partName)}-background, transparent);
13+
background-origin: border-box;
14+
border: var(--${unsafeCSS(partName)}-border, none);
15+
border-radius: var(--${unsafeCSS(partName)}-border-radius, var(--_vaadin-radius-m));
16+
box-sizing: border-box;
17+
color: var(--${unsafeCSS(partName)}-text-color, var(--_vaadin-color-strong));
18+
display: flex;
19+
font-size: var(--${unsafeCSS(partName)}-font-size, inherit);
20+
font-weight: var(--${unsafeCSS(partName)}-font-weight, 500);
21+
gap: var(--${unsafeCSS(partName)}-gap, 0 var(--_vaadin-gap-container-inline));
22+
height: var(--${unsafeCSS(partName)}-height, auto);
23+
outline: calc(var(--vaadin-focus-ring-width) * var(--_focus-ring, 0)) solid var(--vaadin-focus-ring-color);
24+
outline-offset: 1px;
25+
padding: var(--${unsafeCSS(partName)}-padding, var(--_vaadin-padding-container));
26+
-webkit-tap-highlight-color: transparent;
27+
-webkit-user-select: none;
28+
user-select: none;
29+
}
30+
31+
:host([focus-ring]) {
32+
--_focus-ring: 1;
33+
}
34+
35+
:host([hidden]) {
36+
display: none !important;
37+
}
38+
39+
[part='toggle'] {
40+
color: var(--_vaadin-color);
41+
}
42+
43+
@media (prefers-reduced-motion: no-preference) {
44+
[part='toggle'] {
45+
transition-property: rotate;
46+
transition-duration: 150ms;
47+
animation: delay-initial-transition 1ms;
48+
}
49+
50+
@keyframes delay-initial-transition {
51+
0% {
52+
rotate: 0deg;
53+
}
54+
}
55+
}
56+
57+
[part='toggle']::before {
58+
background: currentColor;
59+
content: '';
60+
display: block;
61+
height: var(--vaadin-icon-size, 1lh);
62+
mask-image: var(--_vaadin-icon-chevron-down);
63+
width: var(--vaadin-icon-size, 1lh);
64+
rotate: -90deg;
65+
}
66+
67+
:host([disabled]) {
68+
opacity: 0.5;
69+
cursor: not-allowed;
70+
}
71+
72+
:host([dir='rtl']) [part='toggle']::before {
73+
scale: -1;
74+
}
75+
76+
:host([opened]) [part='toggle'] {
77+
rotate: 90deg;
78+
}
79+
80+
:host([dir='rtl'][opened]) [part='toggle'] {
81+
rotate: -90deg;
82+
}
83+
84+
@media (forced-colors: active) {
85+
[part='toggle']::before {
86+
background: CanvasText;
87+
}
88+
89+
:host([disabled]) {
90+
color: GrayText;
91+
opacity: 1;
92+
}
93+
94+
:host([disabled]) [part='toggle']::before {
95+
background: GrayText;
96+
}
97+
}
98+
`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2025 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import type { CSSResult } from 'lit';
7+
8+
export const detailsSummary: (partName?: string) => CSSResult;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2025 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import { css } from 'lit';
7+
8+
export const detailsSummary = () => css`
9+
:host {
10+
display: block;
11+
outline: none;
12+
white-space: nowrap;
13+
-webkit-user-select: none;
14+
user-select: none;
15+
}
16+
17+
:host([hidden]) {
18+
display: none !important;
19+
}
20+
21+
:host([disabled]) {
22+
pointer-events: none;
23+
}
24+
`;

packages/details/src/vaadin-details-summary.js

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
77
import { ButtonMixin } from '@vaadin/button/src/vaadin-button-mixin.js';
88
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
99
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
10-
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
10+
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
11+
import { detailsSummary } from './vaadin-details-summary-core-styles.js';
12+
13+
registerStyles('vaadin-details-summary', detailsSummary(), { moduleId: 'vaadin-details-summary-styles' });
1114

1215
/**
1316
* The details summary element.
@@ -46,23 +49,6 @@ class DetailsSummary extends ButtonMixin(DirMixin(ThemableMixin(PolymerElement))
4649

4750
static get template() {
4851
return html`
49-
<style>
50-
:host {
51-
display: block;
52-
outline: none;
53-
white-space: nowrap;
54-
-webkit-user-select: none;
55-
user-select: none;
56-
}
57-
58-
:host([hidden]) {
59-
display: none !important;
60-
}
61-
62-
:host([disabled]) {
63-
pointer-events: none;
64-
}
65-
</style>
6652
<span part="toggle" aria-hidden="true"></span>
6753
<div part="content"><slot></slot></div>
6854
`;

packages/details/src/vaadin-lit-details-summary.js

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
* Copyright (c) 2019 - 2025 Vaadin Ltd.
44
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
55
*/
6-
import { css, html, LitElement } from 'lit';
6+
import { html, LitElement } from 'lit';
77
import { ButtonMixin } from '@vaadin/button/src/vaadin-button-mixin.js';
88
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
99
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
1010
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
1111
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
12+
import { detailsSummary } from './vaadin-details-summary-core-styles.js';
1213

1314
/**
1415
* LitElement based version of `<vaadin-details-summary>` web component.
@@ -25,23 +26,7 @@ class DetailsSummary extends ButtonMixin(DirMixin(ThemableMixin(PolylitMixin(Lit
2526
}
2627

2728
static get styles() {
28-
return css`
29-
:host {
30-
display: block;
31-
outline: none;
32-
white-space: nowrap;
33-
-webkit-user-select: none;
34-
user-select: none;
35-
}
36-
37-
:host([hidden]) {
38-
display: none !important;
39-
}
40-
41-
:host([disabled]) {
42-
pointer-events: none;
43-
}
44-
`;
29+
return detailsSummary();
4530
}
4631

4732
static get properties() {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { sendKeys } from '@vaadin/test-runner-commands';
2+
import { fixtureSync } from '@vaadin/testing-helpers/dist/fixture.js';
3+
import { visualDiff } from '@web/test-runner-visual-regression';
4+
import '../../../src/vaadin-details.js';
5+
6+
describe('details', () => {
7+
let div, element;
8+
9+
beforeEach(() => {
10+
div = document.createElement('div');
11+
div.style.padding = '10px';
12+
13+
element = fixtureSync(
14+
`
15+
<vaadin-details>
16+
<vaadin-details-summary slot="summary">Summary</vaadin-details-summary>
17+
<span>Content</span>
18+
</vaadin-details>
19+
`,
20+
div,
21+
);
22+
});
23+
24+
describe('default', () => {
25+
it('basic', async () => {
26+
await visualDiff(div, 'basic');
27+
});
28+
29+
it('opened', async () => {
30+
element.opened = true;
31+
await visualDiff(div, 'opened');
32+
});
33+
34+
it('focus-ring', async () => {
35+
await sendKeys({ press: 'Tab' });
36+
await visualDiff(div, 'focus-ring');
37+
});
38+
39+
it('disabled', async () => {
40+
element.disabled = true;
41+
await visualDiff(div, 'disabled');
42+
});
43+
44+
it('disabled opened', async () => {
45+
element.opened = true;
46+
element.disabled = true;
47+
await visualDiff(div, 'disabled-opened');
48+
});
49+
});
50+
51+
describe('RTL', () => {
52+
before(() => {
53+
document.documentElement.setAttribute('dir', 'rtl');
54+
});
55+
56+
after(() => {
57+
document.documentElement.removeAttribute('dir');
58+
});
59+
60+
it('RTL basic', async () => {
61+
await visualDiff(div, 'rtl-basic');
62+
});
63+
64+
it('RTL opened', async () => {
65+
element.opened = true;
66+
await visualDiff(div, 'rtl-opened');
67+
});
68+
});
69+
});

0 commit comments

Comments
 (0)