Skip to content

Commit

Permalink
feat: add headingLevel property to customize login heading level (#8445)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored Jan 8, 2025
1 parent 821dfc5 commit 2f40092
Show file tree
Hide file tree
Showing 19 changed files with 122 additions and 18 deletions.
9 changes: 8 additions & 1 deletion packages/login/src/vaadin-lit-login-form-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,21 @@ class LoginFormWrapper extends ThemableMixin(PolylitMixin(LitElement)) {
i18n: {
type: Object,
},

/**
* Used to customize the `aria-level` attribute on the heading element.
*/
headingLevel: {
type: Number,
},
};
}

/** @protected */
render() {
return html`
<section part="form">
<div part="form-title" role="heading" aria-level="2">${this.i18n.form.title}</div>
<div part="form-title" role="heading" aria-level="${this.headingLevel}">${this.i18n.form.title}</div>
<div part="error-message" ?hidden="${!this.error}">
<strong part="error-message-title">${this.i18n.errorMessage.title}</strong>
<p part="error-message-description">${this.i18n.errorMessage.message}</p>
Expand Down
1 change: 1 addition & 0 deletions packages/login/src/vaadin-lit-login-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class LoginForm extends LoginFormMixin(ElementMixin(ThemableMixin(PolylitMixin(L
theme="${ifDefined(this._theme)}"
.error="${this.error}"
.i18n="${this.i18n}"
.headingLevel="${this.headingLevel}"
>
<form method="POST" action="${ifDefined(this.action)}" @formdata="${this._onFormData}" slot="form">
<input id="csrf" type="hidden" />
Expand Down
2 changes: 1 addition & 1 deletion packages/login/src/vaadin-lit-login-overlay-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class LoginOverlayWrapper extends LoginOverlayWrapperMixin(ThemableMixin(Polylit
<section part="card">
<div part="brand">
<slot name="title">
<h1 part="title">${this.title}</h1>
<div part="title" role="heading" aria-level="${this.headingLevel}">${this.title}</div>
</slot>
<p part="description">${this.description}</p>
</div>
Expand Down
2 changes: 2 additions & 0 deletions packages/login/src/vaadin-lit-login-overlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class LoginOverlay extends LoginOverlayMixin(ElementMixin(ThemableMixin(PolylitM
.opened="${this.opened}"
.title="${this.title}"
.description="${this.description}"
.headingLevel="${this.headingLevel}"
role="dialog"
focus-trap
with-backdrop
Expand All @@ -51,6 +52,7 @@ class LoginOverlay extends LoginOverlayMixin(ElementMixin(ThemableMixin(PolylitM
.error="${this.error}"
.noAutofocus="${this.noAutofocus}"
.noForgotPassword="${this.noForgotPassword}"
.headingLevel="${this.__computeHeadingLevel(this.headingLevel)}"
.i18n="${this.i18n}"
@login="${this._retargetEvent}"
@forgot-password="${this._retargetEvent}"
Expand Down
9 changes: 8 additions & 1 deletion packages/login/src/vaadin-login-form-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class LoginFormWrapper extends ThemableMixin(PolymerElement) {
static get template() {
return html`
<section part="form">
<div part="form-title" part="form-title" role="heading" aria-level="2">[[i18n.form.title]]</div>
<div part="form-title" part="form-title" role="heading" aria-level$="[[headingLevel]]">[[i18n.form.title]]</div>
<div part="error-message" hidden$="[[!error]]">
<strong part="error-message-title">[[i18n.errorMessage.title]]</strong>
<p part="error-message-description">[[i18n.errorMessage.message]]</p>
Expand Down Expand Up @@ -68,6 +68,13 @@ class LoginFormWrapper extends ThemableMixin(PolymerElement) {
i18n: {
type: Object,
},

/**
* Used to customize the `aria-level` attribute on the heading element.
*/
headingLevel: {
type: Number,
},
};
}
}
Expand Down
8 changes: 7 additions & 1 deletion packages/login/src/vaadin-login-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ class LoginForm extends LoginFormMixin(ElementMixin(ThemableMixin(PolymerElement
width: 100%;
}
</style>
<vaadin-login-form-wrapper id="vaadinLoginFormWrapper" theme$="[[_theme]]" error="[[error]]" i18n="[[i18n]]">
<vaadin-login-form-wrapper
id="vaadinLoginFormWrapper"
theme$="[[_theme]]"
error="[[error]]"
i18n="[[i18n]]"
heading-level="[[headingLevel]]"
>
<form method="POST" action$="[[action]]" on-formdata="_onFormData" slot="form">
<input id="csrf" type="hidden" />
<vaadin-text-field
Expand Down
10 changes: 10 additions & 0 deletions packages/login/src/vaadin-login-mixin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,14 @@ export declare class LoginMixinClass {
* ```
*/
i18n: LoginI18n;

/**
* Sets the root heading level (`aria-level`) for the heading hierarchy. Default value: 1.
* Child headings automatically increment from this base level i.e. standalone login form
* renders its title as `<h1>`, whereas the form in the overlay uses `<h2>`, as the `<h1>`
* element is used by the overlay's own title.
*
* @attr {number} heading-level
*/
headingLevel: number;
}
13 changes: 13 additions & 0 deletions packages/login/src/vaadin-login-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,19 @@ export const LoginMixin = (superClass) =>
},
},

/**
* Sets the root heading level (`aria-level`) for the heading hierarchy. Default value: 1.
* Child headings automatically increment from this base level i.e. standalone login form
* renders its title as `<h1>`, whereas the form in the overlay uses `<h2>`, as the `<h1>`
* element is used by the overlay's own title.
*
* @attr {number} heading-level
*/
headingLevel: {
type: Number,
value: 1,
},

/**
* If set, prevents auto enabling the component when error property is set to true.
* @private
Expand Down
5 changes: 5 additions & 0 deletions packages/login/src/vaadin-login-overlay-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,9 @@ export const LoginOverlayMixin = (superClass) =>
this.append(...teleported);
};
}

/** @private */
__computeHeadingLevel(headingLevel) {
return headingLevel + 1;
}
};
7 changes: 7 additions & 0 deletions packages/login/src/vaadin-login-overlay-wrapper-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ export const LoginOverlayWrapperMixin = (superClass) =>
description: {
type: String,
},

/**
* Used to customize the `aria-level` attribute on the heading element.
*/
headingLevel: {
type: Number,
},
};
}

Expand Down
2 changes: 1 addition & 1 deletion packages/login/src/vaadin-login-overlay-wrapper-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const loginOverlayWrapperStyles = css`
justify-content: flex-end;
}
[part='brand'] h1 {
[part='title'] {
color: inherit;
margin: 0;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/login/src/vaadin-login-overlay-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class LoginOverlayWrapper extends LoginOverlayWrapperMixin(ThemableMixin(Polymer
<section part="card">
<div part="brand">
<slot name="title">
<h1 part="title">[[title]]</h1>
<div part="title" role="heading" aria-level$="[[headingLevel]]">[[title]]</div>
</slot>
<p part="description">[[description]]</p>
</div>
Expand Down
2 changes: 2 additions & 0 deletions packages/login/src/vaadin-login-overlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class LoginOverlay extends LoginOverlayMixin(ElementMixin(ThemableMixin(PolymerE
with-backdrop
title="[[title]]"
description="[[description]]"
heading-level="[[headingLevel]]"
theme$="[[_theme]]"
on-vaadin-overlay-escape-press="_preventClosingLogin"
on-vaadin-overlay-outside-click="_preventClosingLogin"
Expand All @@ -71,6 +72,7 @@ class LoginOverlay extends LoginOverlayMixin(ElementMixin(ThemableMixin(PolymerE
action="[[action]]"
disabled="{{disabled}}"
error="{{error}}"
heading-level="[[__computeHeadingLevel(headingLevel)]]"
no-autofocus="[[noAutofocus]]"
no-forgot-password="[[noForgotPassword]]"
i18n="{{i18n}}"
Expand Down
6 changes: 3 additions & 3 deletions packages/login/test/dom/__snapshots__/login-form.test.snap.js
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ snapshots["vaadin-login-form host noForgotPassword"] =
snapshots["vaadin-login-form shadow default"] =
`<section part="form">
<div
aria-level="2"
aria-level="1"
part="form-title"
role="heading"
>
Expand Down Expand Up @@ -612,7 +612,7 @@ snapshots["vaadin-login-form shadow default"] =
snapshots["vaadin-login-form shadow error"] =
`<section part="form">
<div
aria-level="2"
aria-level="1"
part="form-title"
role="heading"
>
Expand Down Expand Up @@ -647,7 +647,7 @@ snapshots["vaadin-login-form shadow error"] =
snapshots["vaadin-login-form shadow i18n"] =
`<section part="form">
<div
aria-level="2"
aria-level="1"
part="form-title"
role="heading"
>
Expand Down
16 changes: 12 additions & 4 deletions packages/login/test/dom/__snapshots__/login-overlay.test.snap.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,13 @@ snapshots["vaadin-login-overlay shadow default"] =
<section part="card">
<div part="brand">
<slot name="title">
<h1 part="title">
<div
aria-level="1"
part="title"
role="heading"
>
App name
</h1>
</div>
</slot>
<p part="description">
Application description
Expand Down Expand Up @@ -434,9 +438,13 @@ snapshots["vaadin-login-overlay shadow i18n"] =
<section part="card">
<div part="brand">
<slot name="title">
<h1 part="title">
<div
aria-level="1"
part="title"
role="heading"
>
Sovelluksen nimi
</h1>
</div>
</slot>
<p part="description">
Sovelluksen kuvaus
Expand Down
27 changes: 25 additions & 2 deletions packages/login/test/login-overlay.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ describe('title and description', () => {
<vaadin-login-overlay title="New title" description="New description" opened></vaadin-login-overlay>
`);
await nextRender();
headerElement = overlay.$.vaadinLoginOverlayWrapper.shadowRoot.querySelector('[part="brand"] h1');
descriptionElement = overlay.$.vaadinLoginOverlayWrapper.shadowRoot.querySelector('[part="brand"] p');
headerElement = overlay.$.vaadinLoginOverlayWrapper.shadowRoot.querySelector('[part="title"]');
descriptionElement = overlay.$.vaadinLoginOverlayWrapper.shadowRoot.querySelector('[part="description"]');
});

afterEach(() => {
Expand Down Expand Up @@ -192,6 +192,29 @@ describe('title and description', () => {
});
});

describe('heading level', () => {
let overlay, form;

beforeEach(async () => {
overlay = fixtureSync(`<vaadin-login-overlay opened></vaadin-login-overlay>`);
await nextRender();
form = overlay.$.vaadinLoginForm;
});

afterEach(() => {
overlay.opened = false;
});

it('should set login form title heading level based on the overlay', async () => {
expect(overlay.headingLevel).to.equal(1);
expect(form.headingLevel).to.equal(2);

overlay.headingLevel = 2;
await nextUpdate(overlay);
expect(form.headingLevel).to.equal(3);
});
});

describe('title slot', () => {
let overlay, overlayWrapper;

Expand Down
4 changes: 4 additions & 0 deletions packages/login/test/typings/login.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const overlay = document.createElement('vaadin-login-overlay');
assertType<ElementMixinClass>(overlay);
assertType<OverlayClassMixinClass>(overlay);

assertType<number>(overlay.headingLevel);

overlay.addEventListener('login', (event) => {
assertType<LoginOverlayLoginEvent>(event);
assertType<string>(event.detail.username);
Expand All @@ -44,6 +46,8 @@ overlay.addEventListener('description-changed', (event) => {

const form = document.createElement('vaadin-login-form');

assertType<number>(form.headingLevel);

form.addEventListener('login', (event) => {
assertType<LoginFormLoginEvent>(event);
assertType<string>(event.detail.username);
Expand Down
6 changes: 6 additions & 0 deletions packages/login/theme/lumo/vaadin-login-overlay-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ const loginOverlayWrapper = css`
min-height: calc(var(--lumo-size-m) * 5);
}
[part='title'] {
font-size: var(--lumo-font-size-xxxl);
font-weight: 600;
line-height: var(--lumo-line-height-xs);
}
[part='description'] {
line-height: var(--lumo-line-height-s);
color: var(--lumo-tint-70pct);
Expand Down
9 changes: 6 additions & 3 deletions packages/login/theme/material/vaadin-login-overlay-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,14 @@ const loginOverlayWrapper = css`
min-height: 225px;
}
[part='brand'] h1 {
[part='title'] {
color: inherit;
margin: 0;
font-weight: 500;
font-size: 2rem;
line-height: 1.1;
letter-spacing: -0.015em;
text-indent: -0.07em;
}
[part='form'] {
Expand Down Expand Up @@ -91,7 +94,7 @@ const loginOverlayWrapper = css`
padding: 0 2.5rem 5.5rem 2rem;
}
[part='brand'] h1 {
[part='title'] {
font-weight: 500;
font-size: 1.8rem;
text-indent: 1rem;
Expand Down Expand Up @@ -210,7 +213,7 @@ const loginOverlayWrapper = css`
box-sizing: border-box;
}
[part='brand'] h1 {
[part='title'] {
font-size: 2.5em;
}
Expand Down

0 comments on commit 2f40092

Please sign in to comment.