Skip to content

Commit

Permalink
Project import generated by Copybara
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 722886840
  • Loading branch information
jimper authored and copybara-github committed Feb 4, 2025
1 parent 28ecf8a commit 8b5cb7b
Show file tree
Hide file tree
Showing 38 changed files with 116 additions and 48 deletions.
3 changes: 3 additions & 0 deletions src/components/configurator/slot-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ const strings = {
msg('Targeting', {
desc: 'Section containing ad targeting options.',
}),
validationErrorAdUnitPath: () =>
msg('Please specify a valid ad unit path.', {desc: 'Validation error.'}),
};

// Ad unit path validation.
Expand Down Expand Up @@ -382,6 +384,7 @@ export class SlotSettings extends LitElement {
private renderSlotOptions(slot: SampleSlotConfig) {
return html` <configurator-text-field
label="${strings.adUnitLabel()}"
error-text="${strings.validationErrorAdUnitPath()}"
name="adUnit"
pattern="${AD_UNIT_VALIDATION_PATTERN}"
value="${slot.adUnit}"
Expand Down
85 changes: 55 additions & 30 deletions src/components/ui-controls/configurator-text-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@
* limitations under the License.
*/

import '@material/web/textfield/filled-text-field';

import {localized} from '@lit/localize';
import {MdFilledTextField} from '@material/web/textfield/filled-text-field.js';
import {css, html, LitElement} from 'lit';
import {customElement, property, query} from 'lit/decorators.js';
import {ifDefined} from 'lit/directives/if-defined.js';
import {when} from 'lit/directives/when.js';

@localized()
@customElement('configurator-text-field')
export class ConfiguratorTextField extends LitElement {
@query('input') input?: HTMLInputElement;
@query('md-filled-text-field') private input?: MdFilledTextField;

static styles = css`
:host {
Expand All @@ -32,19 +34,14 @@ export class ConfiguratorTextField extends LitElement {
width: 100%;
}
label {
min-width: 125px;
padding: 5px;
padding-inline-start: 0;
}
input {
flex-grow: 1;
padding: 5px;
}
md-filled-text-field {
width: 100%;
input:invalid {
background-color: lightpink;
--md-filled-field-leading-space: 10px;
--md-filled-field-top-space: calc(0.75rem - 2px);
--md-filled-field-trailing-space: 10px;
--md-filled-field-with-label-bottom-space: 4px;
--md-filled-field-with-label-top-space: 2px;
}
`;

Expand All @@ -56,6 +53,12 @@ export class ConfiguratorTextField extends LitElement {
@property({attribute: 'id', type: String})
id = `text-field-${Date.now().toString()}`;

/**
* Custom error text to be shown when the input state is
* invalid. Only used when `pattern` is specified.
*/
@property({attribute: 'error-text', type: String}) errorText?: string;

/**
* User-friendly label for the element.
*/
Expand Down Expand Up @@ -86,27 +89,49 @@ export class ConfiguratorTextField extends LitElement {
if (this.input) this.input.value = this.internalValue;
}

/**
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/reportValidity}
*/
reportValidity() {
if (this.pattern) {
// Disable custom validity to re-enable default constraint validation.
this.setCustomValidity('');

if (!this.input?.validity.valid && this.errorText) {
// Set a custom validation message if the input is invalid.
this.setCustomValidity(this.errorText);
}
}

return this.input?.reportValidity();
}

/**
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setCustomValidity}
*/
setCustomValidity(error: string) {
this.input?.setCustomValidity(error);
}

private handleInput() {
this.internalValue = this.input!.value;

// Fire an event to let the configurator know a value has changed.
this.dispatchEvent(
new CustomEvent('update', {bubbles: true, composed: true}),
);
if (this.reportValidity()) {
// Fire an event to let the configurator know a value has changed.
this.dispatchEvent(
new CustomEvent('update', {bubbles: true, composed: true}),
);
}
}

render() {
return html`${when(
this.label,
() => html`<label for="${this.id}">${this.label}</label>`,
)}
<input
type="text"
id="${this.id}"
name="${ifDefined(this.name)}"
pattern="${ifDefined(this.pattern)}"
value="${this.internalValue}"
@input="${this.handleInput}"
/>`;
return html`<md-filled-text-field
id="${this.id}"
label="${ifDefined(this.label)}"
name="${ifDefined(this.name)}"
pattern="${ifDefined(this.pattern)}"
value="${this.value}"
@input="${this.handleInput}"
></md-filled-text-field>`;
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions test/web/fixtures/configurator-expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,30 @@ export const configuratorExpect = expect.extend({
};
}
},

/**
* Ensures the {@link Locator} points to a valid input element.
*
* @param locator
* @returns
*/
async toBeValid(locator: Locator) {
try {
expect(
await locator.evaluate(
elem => (elem as HTMLInputElement).validity.valid,
),
).toBe(true);

return {
pass: true,
message: () => 'Expected validity state to be invalid, but was valid.',
};
} catch (e) {
return {
pass: false,
message: () => 'Expected validity state to be valid, but was invalid.',
};
}
},
});
9 changes: 9 additions & 0 deletions test/web/fixtures/configurator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ export class Configurator {
return selectElem.locator('md-select-option').filter({hasText: optionText});
}

/**
* Returns configurator text field element(s).
*/
getTextField(label: string, parent: Locator = this.page.locator('body')) {
return parent
.locator(`md-filled-text-field[label="${label}"]`)
.locator('input');
}

/**
* Selects the specified option of the specified configurator select.
*
Expand Down
41 changes: 23 additions & 18 deletions test/web/slot-settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import {outOfPageFormatNames} from '../../src/model/settings.js';

import {Configurator, expect, test} from './fixtures/configurator.js';

const AD_UNIT_SELECTOR = 'input[name=adUnit]';
const CODE_EDITOR_SELECTOR = 'playground-file-editor';

const AD_UNIT_TEXT_FIELD_LABEL = 'Ad unit path';
const SLOT_SETTINGS_TITLE = 'Slot settings';
const FORMAT_SELECT_LABEL = 'Out-of-page format';
const TEMPLATE_SELECT_LABEL = 'Slot template';
Expand All @@ -46,14 +46,15 @@ test.describe('Ad unit path validation', () => {
adUnitPath: string,
shouldBeValid = true,
): Promise<void> {
await configurator.page.locator(AD_UNIT_SELECTOR).fill(adUnitPath);
const adUnitInput = configurator.getTextField(
AD_UNIT_TEXT_FIELD_LABEL,
configurator.getConfigSection(SLOT_SETTINGS_TITLE),
);
await adUnitInput.fill(adUnitPath);

return shouldBeValid
? expect(
configurator.page.locator(`${AD_UNIT_SELECTOR}:invalid`),
).not.toBeVisible()
: expect(
configurator.page.locator(`${AD_UNIT_SELECTOR}:invalid`),
).toBeVisible();
? expect(adUnitInput).toBeValid()
: expect(adUnitInput).not.toBeValid();
}

test('Supported ad unit code characters are valid.', async ({
Expand Down Expand Up @@ -119,10 +120,9 @@ test.describe('Ad template select', () => {
configurator,
page,
}) => {
const slotSettings = configurator.getConfigSection(SLOT_SETTINGS_TITLE);
const templateSelect = configurator.getSelect(
TEMPLATE_SELECT_LABEL,
slotSettings,
configurator.getConfigSection(SLOT_SETTINGS_TITLE),
);
await expect(templateSelect).toBeVisible();

Expand Down Expand Up @@ -158,10 +158,9 @@ test.describe('Ad format select', () => {
configurator,
page,
}) => {
const slotSettings = configurator.getConfigSection(SLOT_SETTINGS_TITLE);
const formatSelect = configurator.getSelect(
FORMAT_SELECT_LABEL,
slotSettings,
configurator.getConfigSection(SLOT_SETTINGS_TITLE),
);
await expect(formatSelect).toBeVisible();

Expand Down Expand Up @@ -189,8 +188,10 @@ test.describe('Ad format exclusions', () => {
configurator: Configurator,
label: string,
) {
const slotSection = configurator.getConfigSection(SLOT_SETTINGS_TITLE);
const select = configurator.getSelect(label, slotSection);
const select = configurator.getSelect(
label,
configurator.getConfigSection(SLOT_SETTINGS_TITLE),
);
await expect(select).toHaveCount(2);

// Ensure that the slot that selects an anchor format can freely switch
Expand Down Expand Up @@ -225,8 +226,10 @@ test.describe('Ad format exclusions', () => {
configurator: Configurator,
label: string,
) {
const slotSection = configurator.getConfigSection(SLOT_SETTINGS_TITLE);
const select = configurator.getSelect(label, slotSection);
const select = configurator.getSelect(
label,
configurator.getConfigSection(SLOT_SETTINGS_TITLE),
);
await expect(select).toHaveCount(2);

// Ensure that slot 1 can select the interstitial format.
Expand All @@ -248,8 +251,10 @@ test.describe('Ad format exclusions', () => {
configurator: Configurator,
label: string,
) {
const slotSection = configurator.getConfigSection(SLOT_SETTINGS_TITLE);
const select = configurator.getSelect(label, slotSection);
const select = configurator.getSelect(
label,
configurator.getConfigSection(SLOT_SETTINGS_TITLE),
);
await expect(select).toHaveCount(2);

// Ensure that slot 1 has access to both side rail formats.
Expand Down

0 comments on commit 8b5cb7b

Please sign in to comment.