Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions packages/base/src/UI5Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,16 @@ abstract class UI5Element extends HTMLElement {
*/
_render() {
const ctor = this.constructor as typeof UI5Element;

// Skip rendering language-aware components while a language change (CLDR + i18n fetch) is
// still in flight. reRenderAllUI5Elements({ languageAware: true }) will re-render them once
// the data is ready. Without this guard, a component added to the render queue *before* the
// language change started (e.g. via renderDeferred) can still call onBeforeRendering and
// onAfterRendering with stale or missing locale data.
if (ctor.getMetadata().isLanguageAware() && getLanguageChangePending()) {
return;
}

const hasIndividualSlots = ctor.getMetadata().hasIndividualSlots();

// restore properties that were initialized before `define` by calling the setter
Expand Down
23 changes: 13 additions & 10 deletions packages/base/src/config/Language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,19 @@ attachConfigurationReset(() => {
let languageChangePending: Promise<void> | null = null;

const startLanguageChange = (language: string): Promise<void> => {
const changePromise = fireLanguageChange(language).then(() => {
if (isBooted()) {
return reRenderAllUI5Elements({ languageAware: true });
}
}).finally(() => {
// Only clear if no newer change has already replaced us
if (languageChangePending === changePromise) {
languageChangePending = null;
}
});
const changePromise = fireLanguageChange(language)
.then(() => {
// Clear if there is no other language change in flight. Re-render all language-aware components
// so they pick up the newly loaded CLDR and i18n data.
if (languageChangePending === changePromise) {
languageChangePending = null;

if (isBooted()) {
return reRenderAllUI5Elements({ languageAware: true });
}
}
});

languageChangePending = changePromise;
return changePromise;
};
Expand Down
18 changes: 13 additions & 5 deletions packages/main/cypress/specs/DatePicker.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import DatePicker from "../../src/DatePicker.js";
import Label from "../../src/Label.js";
import { DATEPICKER_POPOVER_ACCESSIBLE_NAME } from "../../src/generated/i18n/i18n-defaults.js";

describe.skip("Date Picker Tests", () => {
describe("Date Picker Tests", () => {
it("input renders", () => {
cy.mount(<DatePicker></DatePicker>);

Expand Down Expand Up @@ -34,7 +34,9 @@ describe.skip("Date Picker Tests", () => {

it("input receives value in format pattern depending on the set language", () => {
cy.wrap({ setLanguage })
.then(api => api.setLanguage("bg"));
.then(api => {
return api.setLanguage("bg");
});

cy.mount(<DatePicker value="11 декември 2018г." formatPattern="long"></DatePicker>);

Expand All @@ -58,7 +60,9 @@ describe.skip("Date Picker Tests", () => {
.should("have.class", "ui5-dp-item--selected");

cy.wrap({ setLanguage })
.then(api => api.setLanguage("en"));
.then(api => {
return api.setLanguage("en");
});
});

it("custom formatting", () => {
Expand Down Expand Up @@ -318,7 +322,9 @@ describe.skip("Date Picker Tests", () => {

it("respect first day of the week - monday", () => {
cy.wrap({ setLanguage })
.then(api => api.setLanguage("bg"));
.then(api => {
return api.setLanguage("bg");
});

cy.mount(<DatePicker value="фев 6, 2019" formatPattern="MMM d, y"></DatePicker>);

Expand All @@ -338,7 +344,9 @@ describe.skip("Date Picker Tests", () => {
.should("have.class", "ui5-dp-wday6");

cy.wrap({ setLanguage })
.then(api => api.setLanguage("en"));
.then(api => {
return api.setLanguage("en");
});
});

it("if today is 30 jan, clicking next month does not skip feb", () => {
Expand Down
4 changes: 3 additions & 1 deletion packages/main/cypress/specs/DateRangePicker.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ function DateRangePickerTemplate(options: DateTimePickerTemplateOptions) {

describe("DateRangePicker general interaction", () => {
afterEach(() => {
cy.wrap({ setLanguage }).then(api => api.setLanguage("en"));
cy.wrap({ setLanguage }).then(api => {
return api.setLanguage("en");
});
});

it("Custom Validation Error", () => {
Expand Down
95 changes: 47 additions & 48 deletions packages/main/cypress/specs/StepInput.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -626,22 +626,22 @@ describe("StepInput events", () => {
});

describe("StepInput thousand separator formatting", () => {
it("should display value with thousand separator", () => {
cy.mount(
it("should display value with thousand separator", () => {
cy.mount(
<StepInput value={12345}></StepInput>
);

cy.get("[ui5-step-input]")
cy.get("[ui5-step-input]")
.ui5StepInputGetInnerInput()
.should($input => {
const val = $input.val();
// Accepts both comma and dot as separator depending on locale
expect(val).to.match(/12[,.]345/);
});
});

it("should parse formatted value correctly", () => {
cy.mount(
const val = $input.val();
// Accepts both comma and dot as separator depending on locale
expect(val).to.match(/12[,.]345/);
});
});

it("should parse formatted value correctly", () => {
cy.mount(
<StepInput value={12345}></StepInput>
);

Expand All @@ -651,10 +651,10 @@ describe("StepInput thousand separator formatting", () => {
cy.get<StepInput>("@stepInput")
.ui5StepInputGetInnerInput()
.should($input => {
const val = $input.val() as string;
const val = $input.val() as string;
const num = Number(val.replace(/[^\d]/g, ""));
expect(num).to.equal(12345);
});
expect(num).to.equal(12345);
});

cy.get<StepInput>("@stepInput")
.realClick({ "clickCount": 2 })
Expand All @@ -666,18 +666,18 @@ describe("StepInput thousand separator formatting", () => {
cy.get<StepInput>("@stepInput")
.ui5StepInputGetInnerInput()
.should($input => {
const val = $input.val() as string;
expect(val).to.equal("10,000");
});
const val = $input.val() as string;
expect(val).to.equal("10,000");
});

cy.get<StepInput>("@stepInput")
.should("have.prop", "value", 10000);
});
});

it("should update input value when language is changed", () => {
cy.wrap({ setLanguage })
.then(async ({ setLanguage }) => {
await setLanguage("en");
.then(({ setLanguage }) => {
return setLanguage("en");
});

cy.mount(
Expand All @@ -691,24 +691,23 @@ describe("StepInput thousand separator formatting", () => {
.should($input => {
const val = $input.val() as string;
expect(val).to.equal("10,000.56");
});
});

cy.wrap({ setLanguage })
.then(async ({ setLanguage }) => {
await setLanguage("de");
})
.then(() => {
cy.get<StepInput>("@stepInput")
.ui5StepInputGetInnerInput()
.should($input => {
const val = $input.val() as string;
expect(val).to.equal("10.000,56");
});
.then(({ setLanguage }) => {
return setLanguage("de");
});

cy.get<StepInput>("@stepInput")
.ui5StepInputGetInnerInput()
.should($input => {
const val = $input.val() as string;
expect(val).to.equal("10.000,56");
});

cy.wrap({ setLanguage })
.then(async ({ setLanguage }) => {
await setLanguage("en");
.then(({ setLanguage }) => {
return setLanguage("en");
});
});
});
Expand Down Expand Up @@ -780,40 +779,40 @@ describe("StepInput property propagation", () => {
});

it("should increase value on mouse wheel up", () => {
cy.mount(
cy.mount(
<StepInput value={5} step={2}></StepInput>
);

cy.get("[ui5-step-input]")
cy.get("[ui5-step-input]")
.as("stepInput");

cy.get<StepInput>("@stepInput")
cy.get<StepInput>("@stepInput")
.ui5StepInputScrollToChangeValue(7, false);
});
});

it("should decrease value on mouse wheel down", () => {
cy.mount(
it("should decrease value on mouse wheel down", () => {
cy.mount(
<StepInput value={5} step={2}></StepInput>
);

cy.get("[ui5-step-input]")
cy.get("[ui5-step-input]")
.as("stepInput");

cy.get<StepInput>("@stepInput")
cy.get<StepInput>("@stepInput")
.ui5StepInputScrollToChangeValue(3, true);
});
});

it("should not change value when readonly", () => {
cy.mount(
it("should not change value when readonly", () => {
cy.mount(
<StepInput value={5} step={2} readonly={true}></StepInput>
);

cy.get("[ui5-step-input]")
cy.get("[ui5-step-input]")
.as("stepInput");

cy.get<StepInput>("@stepInput")
cy.get<StepInput>("@stepInput")
.ui5StepInputScrollToChangeValue(5, true);
});
});
});

describe("Validation inside form", () => {
Expand Down
3 changes: 1 addition & 2 deletions packages/main/src/Calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -639,8 +639,7 @@ class Calendar extends CalendarPart {
}
}

async onAfterRendering() {
await renderFinished(); // Await for the current picker to render and then ask if it has previous/next pages
onAfterRendering() {
this._previousButtonDisabled = !this._currentPickerDOM._hasPreviousPage();
this._nextButtonDisabled = !this._currentPickerDOM._hasNextPage();

Expand Down
14 changes: 7 additions & 7 deletions packages/main/src/StepInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ class StepInput extends UI5Element implements IFormInputElement {

_updateValueState() {
const isWithinRange = (this.min === undefined || this._parseNumber(this.input.value) >= this.min)
&& (this.max === undefined || this._parseNumber(this.input.value) <= this.max);
&& (this.max === undefined || this._parseNumber(this.input.value) <= this.max);
const isValueWithCorrectPrecision = this._isValueWithCorrectPrecision;
const previousValueState = this.valueState;
const isValid = isWithinRange && isValueWithCorrectPrecision;
Expand Down Expand Up @@ -595,17 +595,17 @@ class StepInput extends UI5Element implements IFormInputElement {
}

/**
* Formats a number with thousands separator based on current locale
* @private
*/
* Formats a number with thousands separator based on current locale
* @private
*/
_formatNumber(value: number): string {
return this.formatter.format(value);
}

/**
* Parses formatted number string back to numeric value
* @private
*/
* Parses formatted number string back to numeric value
* @private
*/
_parseNumber(formattedValue: string): number {
return this.formatter.parse(formattedValue) as number;
}
Expand Down
Loading