diff --git a/packages/component-library/__snapshots__/interaction-tests-form-mt-select--visual-test-ensure-selection-opens-via-indicators-snap.png b/packages/component-library/__snapshots__/interaction-tests-form-mt-select--visual-test-ensure-selection-opens-via-indicators-snap.png
new file mode 100644
index 000000000..d961e2946
Binary files /dev/null and b/packages/component-library/__snapshots__/interaction-tests-form-mt-select--visual-test-ensure-selection-opens-via-indicators-snap.png differ
diff --git a/packages/component-library/src/components/form/_internal/mt-select-base/mt-select-base.vue b/packages/component-library/src/components/form/_internal/mt-select-base/mt-select-base.vue
index babb2f0a4..0ffc0472d 100644
--- a/packages/component-library/src/components/form/_internal/mt-select-base/mt-select-base.vue
+++ b/packages/component-library/src/components/form/_internal/mt-select-base/mt-select-base.vue
@@ -22,7 +22,7 @@
ref="selectWrapper"
class="mt-select__selection"
tabindex="0"
- @click="expand"
+ @click.stop="expand"
@focus="expand"
@keydown.tab="collapse"
@keydown.esc="collapse"
@@ -31,25 +31,32 @@
name="mt-select-selection"
v-bind="{ identification, error, disabled, size, expand, collapse }"
/>
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -354,6 +361,11 @@ $mt-select-focus-transition: all ease-in-out 0.2s;
.mt-select__select-indicator {
flex-shrink: 0;
cursor: pointer;
+ transition: all 0.3s ease-in-out;
+ }
+
+ .mt-select__select-indicator-rotated {
+ transform: rotate(180deg);
}
.mt-select__select-indicator-clear {
diff --git a/packages/component-library/src/components/form/mt-select/mt-select.interactive.stories.ts b/packages/component-library/src/components/form/mt-select/mt-select.interactive.stories.ts
index c45d83d87..7a1e84c91 100644
--- a/packages/component-library/src/components/form/mt-select/mt-select.interactive.stories.ts
+++ b/packages/component-library/src/components/form/mt-select/mt-select.interactive.stories.ts
@@ -407,3 +407,26 @@ export const VisualTestEnsureCorrectMultiSelectionWrapping: MtSelectStory = {
await userEvent.click(loadMoreButton);
},
};
+
+export const VisualTestEnsureSelectionOpensViaIndicators: MtSelectStory = {
+ name: "Should open selection via indicators",
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ await waitUntil(() =>
+ document.querySelector('.mt-select-selection-list__item[title="Option B"]'),
+ );
+
+ // open selection via indicator
+ await userEvent.click(canvas.getByTestId("mt-select__select-indicator"));
+
+ // selection should open
+ const popover = within(
+ document.querySelector(".mt-popover-deprecated__wrapper") as HTMLElement,
+ );
+ await waitUntil(() => popover.getByTestId("mt-select-option--a"));
+
+ // close selection via indicator
+ await userEvent.click(canvas.getByTestId("mt-select__select-indicator"));
+ expect(document.querySelector(".mt-popover-deprecated__wrapper")).toBeNull();
+ },
+};
diff --git a/packages/component-library/src/composables/useI18n.spec.ts b/packages/component-library/src/composables/useI18n.spec.ts
index e305493f8..cf97ea431 100644
--- a/packages/component-library/src/composables/useI18n.spec.ts
+++ b/packages/component-library/src/composables/useI18n.spec.ts
@@ -61,4 +61,64 @@ describe("useI18n", () => {
expect(screen.getByText("Hello, {name}!")).toBeInTheDocument();
});
+
+ it("translates the singular version", () => {
+ render({
+ setup() {
+ const { t } = useI18n({
+ messages: { en: { apple: "apple | apples" } },
+ });
+
+ return { t };
+ },
+ template: "{{ t('apple', { n: 1 }) }}",
+ });
+
+ expect(screen.getByText("apple")).toBeInTheDocument();
+ });
+
+ it("translates the pluralized version", () => {
+ render({
+ setup() {
+ const { t } = useI18n({
+ messages: { en: { apple: "apple | apples" } },
+ });
+
+ return { t };
+ },
+ template: "{{ t('apple', { n: 2 }) }}",
+ });
+
+ expect(screen.getByText("apples")).toBeInTheDocument;
+ });
+
+ it("translates the 'none' version", () => {
+ render({
+ setup() {
+ const { t } = useI18n({
+ messages: { en: { apple: "no apple | apple | apples" } },
+ });
+
+ return { t };
+ },
+ template: "{{ t('apple', { n: 0 }) }}",
+ });
+
+ expect(screen.getByText("no apple")).toBeInTheDocument();
+ });
+
+ it("translates the pluralized version with custom values", () => {
+ render({
+ setup() {
+ const { t } = useI18n({
+ messages: { en: { apple: "no apple | apple | {n} apples" } },
+ });
+
+ return { t };
+ },
+ template: "{{ t('apple', { n: 3 }) }}",
+ });
+
+ expect(screen.getByText("3 apples")).toBeInTheDocument();
+ });
});
diff --git a/packages/component-library/src/composables/useI18n.ts b/packages/component-library/src/composables/useI18n.ts
index afc4426af..d94b57cfd 100644
--- a/packages/component-library/src/composables/useI18n.ts
+++ b/packages/component-library/src/composables/useI18n.ts
@@ -30,6 +30,10 @@ const defaultI18nState = {
const i18nInjectionKey = Symbol("mt-i18n");
+function limit(value: number, max: number) {
+ return Math.min(value, max);
+}
+
export function provideI18n(locale: string = "en") {
const state = {
...defaultI18nState,
@@ -45,16 +49,37 @@ export function useI18n({ messages }: Options) {
const i18n = inject(i18nInjectionKey, defaultI18nState);
function translate(path: string, customValues: Record = {}): string {
+ function resolveCustomKeys(translation: string) {
+ return translation.replace(CUSTOM_VALUE_REGEX, (match: string, key: string) => {
+ return Object.prototype.hasOwnProperty.call(customValues, key)
+ ? customValues[key].toString()
+ : match;
+ });
+ }
+
const translation =
get(messages, `${i18n.locale}.${path}`) || get(messages, `${i18n.defaultLocale}.${path}`);
if (!translation) return path;
- return translation.replace(CUSTOM_VALUE_REGEX, (match: string, key: string) => {
- return Object.prototype.hasOwnProperty.call(customValues, key)
- ? customValues[key].toString()
- : match;
- });
+ const canBePluralized = /\|/.test(translation);
+ if (canBePluralized) {
+ if (typeof customValues.n !== "number")
+ throw new Error('The "n" key is required for pluralization');
+
+ const versions = translation.split("|");
+
+ // "no apple | apple | apples"; The first version is the zero form
+ const includesZeroForm = versions.length === 3;
+
+ const index = limit(includesZeroForm ? customValues.n : customValues.n - 1, 2);
+ const resolvedTranslation = versions.at(index);
+ if (!resolvedTranslation) return path;
+
+ return resolveCustomKeys(resolvedTranslation);
+ }
+
+ return resolveCustomKeys(translation);
}
return { t: translate };