Skip to content

Commit 7965029

Browse files
committed
Merge branch 'minWidthPopover' of github.com:UI5/webcomponents into minWidthPopover
2 parents 0d64ca0 + f29df05 commit 7965029

83 files changed

Lines changed: 1426 additions & 1177 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy-preview.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ jobs:
3737
# so the deploy step would always fail. A skipped check does not block merge.
3838
if: github.event.pull_request.head.repo.full_name == github.repository
3939
runs-on: ubuntu-latest
40+
environment: netlify-preview
4041

4142
steps:
4243
- name: Checkout

.github/workflows/release.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,14 +312,14 @@ jobs:
312312
if: ${{ github.event.inputs.release_type == 'experimental' }}
313313
environment: "npmjs:@ui5/webcomponents"
314314
permissions:
315-
contents: write
315+
contents: read
316316
id-token: write
317317
runs-on: ubuntu-latest
318318
steps:
319319
- name: Checkout
320320
uses: actions/checkout@v4
321321
with:
322-
token: ${{ secrets.UI5_WEBCOMP_BOT_GH_TOKEN }}
322+
token: ${{ secrets.GITHUB_TOKEN }}
323323
fetch-depth: 0
324324

325325
- name: Setup Node
@@ -333,7 +333,7 @@ jobs:
333333

334334
- name: Version Bump
335335
env:
336-
GH_TOKEN: ${{ secrets.UI5_WEBCOMP_BOT_GH_TOKEN }}
336+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
337337
run: |
338338
git config user.name "${{ secrets.UI5_WEBCOMP_BOT_NAME }}"
339339
git config user.email "${{ secrets.UI5_WEBCOMP_BOT_EMAIL }}"

.github/workflows/reset-preview-deploy.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ permissions:
3030
jobs:
3131
cleanup-preview:
3232
runs-on: ubuntu-latest
33-
33+
environment: netlify-preview
34+
3435
steps:
3536
- name: Create empty content
3637
run: |

packages/base/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"devDependencies": {
6565
"@custom-elements-manifest/analyzer": "^0.10.10",
6666
"@openui5/sap.ui.core": "1.146.0",
67-
"@sap-theming/theming-base-content": "11.35.0",
67+
"@sap-theming/theming-base-content": "11.36.3",
6868
"@ui5/cypress-internal": "0.1.0",
6969
"@ui5/webcomponents-tools": "2.23.0-rc.1",
7070
"clean-css": "^5.2.2",

packages/base/src/Render.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,28 @@ const renderDeferred = async (webComponent: UI5Element) => {
3333
await scheduleRenderTask();
3434
};
3535

36+
/**
37+
* Register all web components attached to the DOM
38+
*/
39+
const registerElement = (webComponent: UI5Element) => {
40+
registeredElements.add(webComponent);
41+
};
42+
43+
/**
44+
* Unregister all web components detached from the DOM
45+
*/
46+
const unregisterElement = (webComponent: UI5Element) => {
47+
registeredElements.delete(webComponent);
48+
};
49+
3650
/**
3751
* Renders a component synchronously and adds it to the registry of rendered components
3852
*
3953
* @param webComponent
4054
*/
4155
const renderImmediately = (webComponent: UI5Element) => {
4256
eventProvider.fireEvent("beforeComponentRender", webComponent);
43-
registeredElements.add(webComponent);
57+
registerElement(webComponent);
4458
webComponent._render();
4559
};
4660

@@ -51,7 +65,7 @@ const renderImmediately = (webComponent: UI5Element) => {
5165
*/
5266
const cancelRender = (webComponent: UI5Element) => {
5367
invalidatedWebComponents.remove(webComponent);
54-
registeredElements.delete(webComponent);
68+
unregisterElement(webComponent);
5569
};
5670

5771
/**
@@ -173,6 +187,8 @@ export {
173187
renderDeferred,
174188
renderImmediately,
175189
cancelRender,
190+
registerElement,
191+
unregisterElement,
176192
renderFinished,
177193
reRenderAllUI5Elements,
178194
attachBeforeComponentRender,

packages/base/src/UI5Element.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
renderDeferred,
1919
renderImmediately,
2020
cancelRender,
21+
unregisterElement,
22+
registerElement,
2123
} from "./Render.js";
2224
import { registerTag, isTagRegistered, recordTagRegistrationFailure } from "./CustomElementsRegistry.js";
2325
import { observeDOMNode, unobserveDOMNode } from "./DOMObserver.js";
@@ -332,6 +334,8 @@ abstract class UI5Element extends HTMLElement {
332334

333335
const ctor = this.constructor as typeof UI5Element;
334336

337+
registerElement(this);
338+
335339
this.setAttribute(ctor.getMetadata().getPureTag(), "");
336340
if (ctor.getMetadata().supportsF6FastNavigation() && !this.hasAttribute("data-sap-ui-fastnavgroup")) {
337341
this.setAttribute("data-sap-ui-fastnavgroup", "true");
@@ -351,6 +355,12 @@ abstract class UI5Element extends HTMLElement {
351355
await ctor._definePromise;
352356
}
353357

358+
// Skip rendering while a language change is in progress to avoid rendering with not fully loaded locale data.
359+
// Once the locale data is loaded, the language-aware component will be re-rendered.
360+
if (ctor.getMetadata().isLanguageAware() && getLanguageChangePending()) {
361+
return;
362+
}
363+
354364
if (!this._inDOM) { // Component removed from DOM while _processChildren was running
355365
return;
356366
}
@@ -401,6 +411,7 @@ abstract class UI5Element extends HTMLElement {
401411
this._domRefReadyPromise._deferredResolve!();
402412

403413
cancelRender(this);
414+
unregisterElement(this);
404415
}
405416

406417
/**

packages/fiori/cypress/specs/SideNavigation.cy.tsx

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -939,33 +939,39 @@ describe("Side Navigation interaction", () => {
939939
</SideNavigation>
940940
);
941941

942-
cy.get("#parentItem").realClick();
943-
[
942+
const cases: Array<{ selector: () => Cypress.Chainable, expectedCallCount: number }> = [
944943
{
945-
element: cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-item][text='2']").shadow()
946-
.find(".ui5-sn-item"),
944+
selector: () => cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-item][text='2']").shadow().find(".ui5-sn-item"),
947945
expectedCallCount: 1,
948946
},
949947
{
950-
element: cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-sub-item][text='2.1']"),
948+
selector: () => cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-sub-item][text='2.1']"),
951949
expectedCallCount: 1,
952950
},
953951
{
954-
element: cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-sub-item][text='2.2']"),
952+
selector: () => cy.get("#sideNav").shadow().find("[ui5-responsive-popover] [ui5-side-navigation-sub-item][text='2.2']"),
955953
expectedCallCount: 0,
956954
},
957-
].forEach(({ element, expectedCallCount }) => {
955+
];
956+
957+
cases.forEach(({ selector, expectedCallCount }) => {
958958
cy.get("#sideNav")
959959
.then(sideNav => {
960960
sideNav.get(0).addEventListener("ui5-selection-change", cy.stub().as("selectionChangeHandler"));
961961
});
962962

963-
// act
963+
// open the popover fresh each iteration
964964
cy.get("#parentItem").realClick();
965-
element.realClick();
965+
cy.get("#sideNav").shadow().find("[ui5-responsive-popover]").should("be.visible");
966+
967+
// act
968+
selector().realClick();
966969

967970
// assert
968-
cy.get("@selectionChangeHandler", { timeout: 1000 }).should("have.callCount", expectedCallCount);
971+
cy.get("@selectionChangeHandler").should("have.callCount", expectedCallCount);
972+
973+
// close popover before next iteration (click outside)
974+
cy.get("body").click(0, 0);
969975
});
970976
});
971977

@@ -978,6 +984,8 @@ describe("Side Navigation interaction", () => {
978984
);
979985

980986
cy.get("#item").realClick();
987+
// wait for first selection to settle before clicking again
988+
cy.get("@selectionChangeHandler").should("have.been.calledOnce");
981989
cy.get("#item").realClick();
982990

983991
cy.get("@selectionChangeHandler").should("have.been.calledOnce");
@@ -1021,14 +1029,17 @@ describe("Side Navigation interaction", () => {
10211029
.realClick();
10221030

10231031
cy.get("@overflowMenu")
1024-
.should("be.not.visible");
1032+
.should("not.be.visible");
10251033

10261034
cy.get("[ui5-side-navigation-item][text='Home 6']")
10271035
.should("be.focused");
10281036

10291037
cy.get("@itemOverflow")
10301038
.realClick();
10311039

1040+
cy.get("@overflowMenu")
1041+
.should("be.visible");
1042+
10321043
cy.get("@overflowMenu")
10331044
.find("[ui5-navigation-menu-item][text='Home 7']")
10341045
.realClick();

packages/fiori/src/i18n/messagebundle_mk.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ DYNAMIC_PAGE_ARIA_LABEL_EXPAND_HEADER=Прошири го заглавието
1212

1313
DYNAMIC_PAGE_ARIA_LABEL_SNAP_HEADER=Скриј го заглавието
1414

15-
DYNAMIC_PAGE_ARIA_LABEL_PIN_HEADER=Фиксирај го заглавието
15+
DYNAMIC_PAGE_ARIA_LABEL_PIN_HEADER=Закачи го заглавието
1616

1717
DYNAMIC_PAGE_ARIA_LABEL_UNPIN_HEADER=Поништи го фиксирањето на заглавието
1818

packages/main/cypress/specs/ColorPicker.cy.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,62 @@ describe("Color Picker general interaction tests", () => {
306306
});
307307
});
308308

309+
describe("Color Picker font-size scaling", () => {
310+
afterEach(() => {
311+
cy.document().then(doc => {
312+
doc.documentElement.style.fontSize = "";
313+
});
314+
});
315+
316+
it("should select white at bottom-right corner with 14px root font-size", () => {
317+
cy.document().then(doc => {
318+
doc.documentElement.style.fontSize = "14px";
319+
});
320+
321+
cy.mount(<ColorPicker></ColorPicker>);
322+
323+
cy.get("[ui5-color-picker]").as("colorPicker");
324+
325+
cy.get<ColorPicker>("@colorPicker")
326+
.shadow()
327+
.find(".ui5-color-picker-main-color")
328+
.realClick({ position: "bottomRight" });
329+
330+
cy.get<ColorPicker>("@colorPicker")
331+
.ui5ColorPickerToggleColorMode();
332+
333+
cy.get<ColorPicker>("@colorPicker")
334+
.ui5ColorPickerValidateInput("#saturation", "0");
335+
336+
cy.get<ColorPicker>("@colorPicker")
337+
.ui5ColorPickerValidateInput("#light", "100");
338+
});
339+
340+
it("should select black at top-left corner with 20px root font-size", () => {
341+
cy.document().then(doc => {
342+
doc.documentElement.style.fontSize = "20px";
343+
});
344+
345+
cy.mount(<ColorPicker></ColorPicker>);
346+
347+
cy.get("[ui5-color-picker]").as("colorPicker");
348+
349+
cy.get<ColorPicker>("@colorPicker")
350+
.shadow()
351+
.find(".ui5-color-picker-main-color")
352+
.realClick({ position: "topLeft" });
353+
354+
cy.get<ColorPicker>("@colorPicker")
355+
.ui5ColorPickerToggleColorMode();
356+
357+
cy.get<ColorPicker>("@colorPicker")
358+
.ui5ColorPickerValidateInput("#saturation", "100");
359+
360+
cy.get<ColorPicker>("@colorPicker")
361+
.ui5ColorPickerValidateInput("#light", "0");
362+
});
363+
});
364+
309365
describe("Color Picker accessibility tests", () => {
310366
it("should show correct accessibility info for RGB inputs", () => {
311367
cy.mount(<ColorPicker></ColorPicker>);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import "../../src/Assets.js";
2+
import { setLanguage } from "@ui5/webcomponents-base/dist/config/Language.js";
3+
import DatePicker from "../../src/DatePicker.js";
4+
5+
// Timestamps for Feb 2019 calendar checks
6+
const timestamp_3_Feb_2019 = 1549152000;
7+
const timestamp_28_Jan_2019 = 1548633600;
8+
9+
describe("DatePicker CLDR race condition", () => {
10+
it("mounts correctly when setLanguage is called without await before mount", () => {
11+
// Capture the promise but do NOT await it — this is the race condition.
12+
// Before the fix, the component rendered before CLDR data loaded → crash.
13+
// After the fix, connectedCallback bails early and reRenderAllUI5Elements
14+
// picks up the registered element once CLDR resolves.
15+
let languageReady: Promise<void>;
16+
cy.then(() => {
17+
languageReady = setLanguage("bg");
18+
});
19+
20+
cy.mount(<DatePicker value="фев 6, 2019" formatPattern="MMM d, y"></DatePicker>);
21+
22+
// Now wait for the language change to settle so locale data is applied
23+
cy.then(() => languageReady);
24+
25+
cy.get("[ui5-date-picker]")
26+
.as("datePicker")
27+
.ui5DatePickerValueHelpIconPress();
28+
29+
// Monday (bg locale) should be the first displayed date — week starts Jan 28
30+
cy.get<DatePicker>("@datePicker")
31+
.ui5DatePickerGetFirstDisplayedDate()
32+
.should("have.attr", "data-sap-timestamp", timestamp_28_Jan_2019.toString());
33+
34+
// Feb 3 should fall on Sunday (last day in Mon-first week = wday6)
35+
cy.get<DatePicker>("@datePicker")
36+
.ui5DatePickerGetPopoverDate(timestamp_3_Feb_2019)
37+
.should("have.class", "ui5-dp-wday6");
38+
});
39+
});

0 commit comments

Comments
 (0)