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(FormLayout): add FormItem and labelsAside support to autoResponsive mode #8729

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bd7777e
chore: add form-layout-auto-responsive dev page (#8694)
vursen Feb 19, 2025
f8d30f6
feat: introduce autoResponsive mode (#8689)
vursen Feb 20, 2025
a4ea1f5
chore: improve form layout dev page examples (#8704)
vursen Feb 20, 2025
447f133
Merge branch 'main' into feat/auto-responsive-form-layout
vursen Feb 24, 2025
e908313
feat: add FormRow and br support to autoResponsive mode (#8699)
vursen Feb 24, 2025
f6471b7
test: assert gridColumnStart instead of --_column-start (#8726)
vursen Feb 24, 2025
1b10494
test: improve visual tests to be more illustrative (#8725)
vursen Feb 24, 2025
9ae63e5
fix: do not set position and max-width when dialog size is set (#8722)
web-padawan Feb 24, 2025
f66cce2
feat: add FormItem and labelsAside support to autoResponsive mode
vursen Feb 24, 2025
1980724
add screenshot tests, expand form item to full width
vursen Feb 24, 2025
dd9b9c1
add snapshot tests
vursen Feb 24, 2025
e68f5b1
fix linter errors
vursen Feb 24, 2025
4547b11
add unit tests, screenshot tests
vursen Feb 25, 2025
0e2bfc5
revert unnecessary changes
vursen Feb 25, 2025
72d3d93
add integration test with dialog
vursen Feb 25, 2025
482c5e9
test: extract assertFormLayoutGrid test helper
vursen Feb 25, 2025
086d2d5
test: extract assertFormLayoutGrid test helper (#8731)
vursen Feb 25, 2025
20a86b2
add integration tests with horizontal layout
vursen Feb 25, 2025
9f0ae3a
Merge remote-tracking branch 'origin/feat/auto-responsive-form-layout…
vursen Feb 25, 2025
88c715c
test: make autoResponsive tests more concise (#8733)
vursen Feb 25, 2025
a15a3d1
Merge remote-tracking branch 'origin/feat/auto-responsive-form-layout…
vursen Feb 25, 2025
456cece
assert label position relative to input
vursen Feb 26, 2025
cd1b489
Merge remote-tracking branch 'origin/main' into feat/auto-responsive-…
vursen Feb 26, 2025
5e37990
rename CSS variables
vursen Feb 26, 2025
84b5adc
update snapshots
vursen Feb 26, 2025
ac6a144
rename CSS variables for better clarity
vursen Feb 26, 2025
f6e352f
fix integration tests, use String.repeat
vursen Feb 26, 2025
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
31 changes: 31 additions & 0 deletions dev/form-layout-auto-responsive.html
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,37 @@ <h1>autoResponsive inside Dialog + expandColumns</h1>
></vaadin-dialog>
<vaadin-button @click="${this.openPrevSiblingDialog}">Open</vaadin-button>

<h1>autoResponsive inside Dialog + labelsAside</h1>
<vaadin-dialog
${dialogRenderer(
(dialog) => html`
<vaadin-form-layout auto-responsive auto-rows max-columns="2" labels-aside>
<vaadin-form-item>
<label slot="label"> First name </label>
<vaadin-text-field></vaadin-text-field>
</vaadin-form-item>
<vaadin-form-item>
<label slot="label"> Last name </label>
<vaadin-text-field></vaadin-text-field>
</vaadin-form-item>
<vaadin-form-item colspan="2">
<label slot="label"> Username </label>
<vaadin-text-field colspan="2"></vaadin-text-field>
</vaadin-form-item>
<vaadin-form-item>
<label slot="label"> Password </label>
<vaadin-password-field></vaadin-password-field>
</vaadin-form-item>
<vaadin-form-item>
<label slot="label"> Confirm </label>
<vaadin-password-field></vaadin-password-field>
</vaadin-form-item>
</vaadin-form-layout>
`,
)}
></vaadin-dialog>
<vaadin-button @click="${this.openPrevSiblingDialog}">Open</vaadin-button>

<h1>autoResponsive inside Dialog 80%</h1>
<vaadin-dialog
width="80%"
Expand Down
15 changes: 15 additions & 0 deletions packages/form-layout/src/vaadin-form-layout-mixin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,21 @@ export declare class FormLayoutMixinClass {
*/
autoRows: boolean;

/**
* When enabled with `autoResponsive`, `<vaadin-form-item>` prefers positioning
* labels beside the fields. If the layout is too narrow to fit a single column
* with side labels, they revert to their default position above the fields.
*
* To customize the label width and the gap between the label and the field,
* use the following CSS properties:
*
* - `--vaadin-form-layout-label-width`
* - `--vaadin-form-layout-label-spacing`
*
* @attr {boolean} labels-aside
*/
labelsAside: boolean;

/**
* Update the layout.
*/
Expand Down
30 changes: 29 additions & 1 deletion packages/form-layout/src/vaadin-form-layout-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,26 @@ export const FormLayoutMixin = (superClass) =>
reflectToAttribute: true,
},

/**
* When enabled with `autoResponsive`, `<vaadin-form-item>` prefers positioning
* labels beside the fields. If the layout is too narrow to fit a single column
* with side labels, they revert to their default position above the fields.
*
* To customize the label width and the gap between the label and the field,
* use the following CSS properties:
*
* - `--vaadin-form-layout-label-width`
* - `--vaadin-form-layout-label-spacing`
*
* @attr {boolean} labels-aside
*/
labelsAside: {
type: Boolean,
sync: true,
value: false,
reflectToAttribute: true,
},

/**
* Current number of columns in the layout
* @private
Expand Down Expand Up @@ -392,8 +412,10 @@ export const FormLayoutMixin = (superClass) =>

/** @private */
__updateCSSGridLayout() {
let resetColumn = false;
const fitsLabelsAside = this.offsetWidth >= this.__columnWidthWithLabelsAside;
this.$.layout.toggleAttribute('fits-labels-aside', this.labelsAside && fitsLabelsAside);

let resetColumn = false;
[...this.children]
.flatMap((child) => {
return child.localName === 'vaadin-form-row' ? [...child.children] : child;
Expand Down Expand Up @@ -448,4 +470,10 @@ export const FormLayoutMixin = (superClass) =>
this.style.removeProperty('--_max-columns');
}
}

/** @private */
get __columnWidthWithLabelsAside() {
const { backgroundPositionY } = getComputedStyle(this.$.layout, '::before');
return parseFloat(backgroundPositionY);
}
};
55 changes: 42 additions & 13 deletions packages/form-layout/src/vaadin-form-layout-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,22 @@ export const formLayoutStyles = css`

:host([auto-responsive]) {
--_column-gap: var(--vaadin-form-layout-column-spacing);
--_column-width-with-labels-above: var(--_column-width);
--_column-width-with-labels-aside: calc(
var(--_column-width) + var(--vaadin-form-layout-label-width) + var(--vaadin-form-layout-label-spacing)
);
--_max-total-gap-width: calc((var(--_max-columns) - 1) * var(--_column-gap));
--_max-total-col-width: calc(var(--_max-columns) * var(--_column-width));
--_max-total-column-width: calc(var(--_max-columns) * var(--_column-width-with-labels-above));

display: flex;
min-width: var(--_column-width);
min-width: var(--_column-width-with-labels-above);
}

:host([auto-responsive]) #layout {
--_effective-column-width: var(--_column-width-with-labels-above);

display: grid;
grid-template-columns: repeat(auto-fit, var(--_column-width));
grid-template-columns: repeat(auto-fit, var(--_effective-column-width));
justify-items: start;
gap: var(--vaadin-form-layout-row-spacing) var(--_column-gap);

Expand All @@ -71,7 +77,7 @@ export const formLayoutStyles = css`
number of columns inside <vaadin-overlay>, which creates a new stacking context
without a predefined width.
*/
width: calc(var(--_max-total-col-width) + var(--_max-total-gap-width));
width: calc(var(--_max-total-column-width) + var(--_max-total-gap-width));

/*
Firefox requires min-width on both :host and #layout to allow the layout
Expand All @@ -80,13 +86,33 @@ export const formLayoutStyles = css`
min-width: inherit;
}

:host([auto-responsive]) #layout::before {
background-position-y: var(--_column-width-with-labels-aside);
}

:host([auto-responsive]) #layout ::slotted(*) {
--_label-position-above: initial;
--_label-position-aside: ' ';

grid-column-start: 1;
}

:host([auto-responsive][auto-rows]) #layout ::slotted(*) {
grid-column-start: var(--_column-start, auto);
}

:host([auto-responsive][labels-aside]) {
--_max-total-column-width: calc(var(--_max-columns) * var(--_column-width-with-labels-aside));
}

:host([auto-responsive][labels-aside]) #layout[fits-labels-aside] {
--_effective-column-width: var(--_column-width-with-labels-aside);
}

:host([auto-responsive][labels-aside]) #layout[fits-labels-aside] ::slotted(*) {
--_label-position-above: ' ';
--_label-position-aside: initial;
}
`;

export const formRowStyles = css`
Expand All @@ -109,30 +135,33 @@ export const formRowStyles = css`

export const formItemStyles = css`
:host {
--_label-position-above: ' ';
--_label-position-aside: initial;

display: inline-flex;
flex-direction: row;
align-items: baseline;
align-items: var(--_label-position-aside, baseline);
flex-flow: var(--_label-position-above, column) nowrap;
justify-self: stretch;
margin: calc(0.5 * var(--vaadin-form-item-row-spacing, var(--vaadin-form-layout-row-spacing, 1em))) 0;
}

:host([label-position='top']) {
flex-direction: column;
align-items: stretch;
--_label-position-above: initial;
--_label-position-aside: ' ';
}

:host([hidden]) {
display: none !important;
}

#label {
width: var(--vaadin-form-item-label-width, var(--vaadin-form-layout-label-width, 8em));
width: var(
--_label-position-aside,
var(--vaadin-form-item-label-width, var(--vaadin-form-layout-label-width, 8em))
);
flex: 0 0 auto;
}

:host([label-position='top']) #label {
width: auto;
}

#spacing {
width: var(--vaadin-form-item-label-spacing, var(--vaadin-form-layout-label-spacing, 1em));
flex: 0 0 auto;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* @web/test-runner snapshot v1 */
export const snapshots = {};

snapshots["vaadin-form-layout auto-responsive basic host default"] =
snapshots["vaadin-form-layout auto-responsive basic host default"] =
Copy link
Member

Choose a reason for hiding this comment

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

I think these whitespace changes should not be needed. Please consider removing form-layout.test.snap.js file and running yarn update:snapshots --group form-layout again, then the file will be regenerated correctly.

This would rearrange some snapshots to match the order of the newly added tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, done.

`<vaadin-form-layout
auto-responsive=""
style="--_column-width: 13em; --_max-columns: 10;"
Expand All @@ -14,7 +14,7 @@ snapshots["vaadin-form-layout auto-responsive basic host default"] =
`;
/* end snapshot vaadin-form-layout auto-responsive basic host default */

snapshots["vaadin-form-layout auto-responsive basic host maxColumns"] =
snapshots["vaadin-form-layout auto-responsive basic host maxColumns"] =
`<vaadin-form-layout
auto-responsive=""
style="--_column-width: 13em; --_max-columns: 3;"
Expand All @@ -27,7 +27,7 @@ snapshots["vaadin-form-layout auto-responsive basic host maxColumns"] =
`;
/* end snapshot vaadin-form-layout auto-responsive basic host maxColumns */

snapshots["vaadin-form-layout auto-responsive basic host columnWidth"] =
snapshots["vaadin-form-layout auto-responsive basic host columnWidth"] =
`<vaadin-form-layout
auto-responsive=""
style="--_column-width: 15em; --_max-columns: 10;"
Expand All @@ -40,15 +40,15 @@ snapshots["vaadin-form-layout auto-responsive basic host columnWidth"] =
`;
/* end snapshot vaadin-form-layout auto-responsive basic host columnWidth */

snapshots["vaadin-form-layout auto-responsive basic shadow default"] =
snapshots["vaadin-form-layout auto-responsive basic shadow default"] =
`<div id="layout">
<slot id="slot">
</slot>
</div>
`;
/* end snapshot vaadin-form-layout auto-responsive basic shadow default */

snapshots["vaadin-form-layout auto-responsive autoRows default"] =
snapshots["vaadin-form-layout auto-responsive autoRows default"] =
`<vaadin-form-layout
auto-responsive=""
auto-rows=""
Expand All @@ -70,3 +70,36 @@ snapshots["vaadin-form-layout auto-responsive autoRows default"] =
`;
/* end snapshot vaadin-form-layout auto-responsive autoRows default */

snapshots["vaadin-form-layout auto-responsive basic host labelsAside"] =
`<vaadin-form-layout
auto-responsive=""
labels-aside=""
style="--_column-width: 13em; --_max-columns: 10;"
>
<input placeholder="First name">
<input placeholder="Last name">
<input placeholder="Email">
<input placeholder="Phone">
</vaadin-form-layout>
`;
/* end snapshot vaadin-form-layout auto-responsive basic host labelsAside */

snapshots["vaadin-form-layout auto-responsive basic shadow labelsAside"] =
`<div
fits-labels-aside=""
id="layout"
>
<slot id="slot">
</slot>
</div>
`;
/* end snapshot vaadin-form-layout auto-responsive basic shadow labelsAside */

snapshots["vaadin-form-layout auto-responsive basic shadow labelsAside with too narrow layout"] =
`<div id="layout">
<slot id="slot">
</slot>
</div>
`;
/* end snapshot vaadin-form-layout auto-responsive basic shadow labelsAside with too narrow layout */

21 changes: 20 additions & 1 deletion packages/form-layout/test/dom/form-layout.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from '@vaadin/chai-plugins';
import { fixtureSync, nextFrame } from '@vaadin/testing-helpers';
import { fixtureSync, nextFrame, nextResize } from '@vaadin/testing-helpers';
import '../../src/vaadin-form-layout.js';

describe('vaadin-form-layout', () => {
Expand Down Expand Up @@ -33,12 +33,31 @@ describe('vaadin-form-layout', () => {
layout.columnWidth = '15em';
await expect(layout).dom.to.equalSnapshot();
});

it('labelsAside', async () => {
layout.labelsAside = true;
await nextResize(layout);
await expect(layout).dom.to.equalSnapshot();
});
});

describe('shadow', () => {
it('default', async () => {
await expect(layout).shadowDom.to.equalSnapshot();
});

it('labelsAside', async () => {
layout.labelsAside = true;
await nextResize(layout);
await expect(layout).shadowDom.to.equalSnapshot();
});

it('labelsAside with too narrow layout', async () => {
layout.style.width = '8em';
layout.labelsAside = true;
await nextResize(layout);
await expect(layout).shadowDom.to.equalSnapshot();
});
});
});

Expand Down
2 changes: 1 addition & 1 deletion packages/form-layout/test/form-item.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe('form-item', () => {
item.setAttribute('label-position', 'top');

expect(getComputedStyle(item).getPropertyValue('flex-direction')).to.equal('column');
expect(getComputedStyle(item).getPropertyValue('align-items')).to.equal('stretch');
expect(getComputedStyle(item).getPropertyValue('align-items')).to.equal('normal');
});
});

Expand Down
Loading