Skip to content

feat(input-number, input, input-text): add built-in translations #5458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5 changes: 4 additions & 1 deletion src/components/input-number/input-number.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
labelable,
reflects,
renders,
hidden
hidden,
t9n
} from "../../tests/commonTests";
import { html } from "../../../support/formatting";
import { letterKeys, numberKeys } from "../../utils/key";
Expand Down Expand Up @@ -1350,4 +1351,6 @@ describe("calcite-input-number", () => {
testValue: 5,
submitsOnEnter: true
}));

it("supports translation", () => t9n("calcite-input-number"));
});
64 changes: 55 additions & 9 deletions src/components/input-number/input-number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from "@stencil/core";
import { getElementDir, getElementProp, getSlotted, setRequestedIcon } from "../../utils/dom";

import { CSS, SLOTS, TEXT } from "./resources";
import { CSS, SLOTS } from "./resources";
import { InputPlacement } from "./interfaces";
import { connectLabel, disconnectLabel, getLabelText, LabelableComponent } from "../../utils/label";
import {
Expand All @@ -35,10 +35,18 @@ import {
} from "../../utils/locale";
import { numberKeys } from "../../utils/key";
import { isValidNumber, parseNumberString, sanitizeNumberString } from "../../utils/number";
import { CSS_UTILITY, TEXT as COMMON_TEXT } from "../../utils/resources";
import { CSS_UTILITY } from "../../utils/resources";
import { decimalPlaces } from "../../utils/math";
import { createObserver } from "../../utils/observers";
import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
import {
connectMessages,
disconnectMessages,
setUpMessages,
T9nComponent,
updateMessages
} from "../../utils/t9n";
import { Messages } from "./assets/input-number/t9n";

type NumberNudgeDirection = "up" | "down";
type setNumberValueOrigin = "initial" | "connected" | "user" | "reset" | "direct";
Expand All @@ -49,10 +57,16 @@ type setNumberValueOrigin = "initial" | "connected" | "user" | "reset" | "direct
@Component({
tag: "calcite-input-number",
styleUrl: "input-number.scss",
shadow: true
shadow: true,
assetsDirs: ["assets"]
})
export class InputNumber
implements LabelableComponent, FormComponent, InteractiveComponent, LocalizedComponent
implements
LabelableComponent,
FormComponent,
InteractiveComponent,
LocalizedComponent,
T9nComponent
{
//--------------------------------------------------------------------------
//
Expand Down Expand Up @@ -114,15 +128,18 @@ export class InputNumber

/**
* A text label that will appear on the clear button for screen readers.
*
* @deprecated – translations are now built-in, if you need to override a string, please use `messageOverrides`.
*/
@Prop() intlClear?: string;

/**
* Accessible name that will appear while loading.
*
* @deprecated – translations are now built-in, if you need to override a string, please use `messageOverrides`.
* @default "Loading"
*/
@Prop() intlLoading?: string = COMMON_TEXT.loading;
@Prop() intlLoading?: string;

/** When `true`, the icon will be flipped when the element direction is right-to-left (`"rtl"`). */
@Prop({ reflect: true }) iconFlipRtl = false;
Expand Down Expand Up @@ -252,6 +269,26 @@ export class InputNumber
/** The component's value. */
@Prop({ mutable: true }) value = "";

/**
* Made into a prop for testing purposes only
*
* @internal
*/
@Prop({ mutable: true }) messages: Messages;

/**
* Use this property to override individual strings used by the component.
*/
@Prop({ mutable: true }) messageOverrides: Partial<Messages>;

@Watch("intlClear")
@Watch("intlLoading")
@Watch("defaultMessages")
@Watch("messageOverrides")
onMessagesChange(): void {
/** referred in t9n util */
}

@Watch("value")
valueWatcher(newValue: string, previousValue: string): void {
if (!this.userChangedValue) {
Expand Down Expand Up @@ -323,6 +360,13 @@ export class InputNumber

@State() effectiveLocale = "";

@Watch("effectiveLocale")
effectiveLocaleChange(): void {
updateMessages(this, this.effectiveLocale);
}

@State() defaultMessages: Messages;

@State() localizedValue: string;

//--------------------------------------------------------------------------
Expand All @@ -333,7 +377,7 @@ export class InputNumber

connectedCallback(): void {
connectLocalized(this);

connectMessages(this);
this.scale = getElementProp(this.el, "scale", this.scale);
this.status = getElementProp(this.el, "status", this.status);
this.inlineEditableEl = this.el.closest("calcite-inline-editable");
Expand All @@ -360,15 +404,17 @@ export class InputNumber
disconnectLabel(this);
disconnectForm(this);
disconnectLocalized(this);
disconnectMessages(this);

this.mutationObserver?.disconnect();
this.el.removeEventListener("calciteInternalHiddenInputChange", this.hiddenInputChangeHandler);
}

componentWillLoad(): void {
async componentWillLoad(): Promise<void> {
this.maxString = this.max?.toString();
this.minString = this.min?.toString();
this.requestedIcon = setRequestedIcon({}, this.icon, "number");
await setUpMessages(this);
}

componentShouldUpdate(newValue: string, oldValue: string, property: string): boolean {
Expand Down Expand Up @@ -791,13 +837,13 @@ export class InputNumber
const dir = getElementDir(this.el);
const loader = (
<div class={CSS.loader}>
<calcite-progress label={this.intlLoading} type="indeterminate" />
<calcite-progress label={this.messages.loading} type="indeterminate" />
</div>
);

const inputClearButton = (
<button
aria-label={this.intlClear || TEXT.clear}
aria-label={this.messages.clear}
class={CSS.clearButton}
disabled={this.disabled || this.readOnly}
onClick={this.clearInputValue}
Expand Down
4 changes: 0 additions & 4 deletions src/components/input-number/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,3 @@ export const CSS = {
export const SLOTS = {
action: "action"
};

export const TEXT = {
clear: "Clear value"
};
5 changes: 4 additions & 1 deletion src/components/input-text/input-text.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
labelable,
reflects,
renders,
hidden
hidden,
t9n
} from "../../tests/commonTests";
import { html } from "../../../support/formatting";
import { TEXT } from "./resources";
Expand Down Expand Up @@ -364,4 +365,6 @@ describe("calcite-input-text", () => {
});

it("is form-associated", () => formAssociated("calcite-input-text", { testValue: "test", submitsOnEnter: true }));

it("supports translation", () => t9n("calcite-input-text"));
});
76 changes: 66 additions & 10 deletions src/components/input-text/input-text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import {
Host,
Method,
Prop,
State,
VNode,
Watch
} from "@stencil/core";
import { getElementDir, getElementProp, getSlotted, setRequestedIcon } from "../../utils/dom";

import { CSS, SLOTS, TEXT } from "./resources";
import { CSS, SLOTS } from "./resources";
import { Position } from "../interfaces";
import { LabelableComponent, connectLabel, disconnectLabel, getLabelText } from "../../utils/label";
import {
Expand All @@ -23,10 +24,18 @@ import {
HiddenFormInputSlot,
submitForm
} from "../../utils/form";
import { CSS_UTILITY, TEXT as COMMON_TEXT } from "../../utils/resources";

import { CSS_UTILITY } from "../../utils/resources";
import { createObserver } from "../../utils/observers";
import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
import { connectLocalized, disconnectLocalized, LocalizedComponent } from "../../utils/locale";
import {
connectMessages,
disconnectMessages,
setUpMessages,
T9nComponent,
updateMessages
} from "../../utils/t9n";
import { Messages } from "./assets/input-text/t9n";

type SetValueOrigin = "initial" | "connected" | "user" | "reset" | "direct";

Expand All @@ -36,9 +45,17 @@ type SetValueOrigin = "initial" | "connected" | "user" | "reset" | "direct";
@Component({
tag: "calcite-input-text",
styleUrl: "input-text.scss",
shadow: true
shadow: true,
assetsDirs: ["assets"]
})
export class InputText implements LabelableComponent, FormComponent, InteractiveComponent {
export class InputText
implements
LabelableComponent,
FormComponent,
InteractiveComponent,
LocalizedComponent,
T9nComponent
{
//--------------------------------------------------------------------------
//
// Element
Expand Down Expand Up @@ -94,15 +111,18 @@ export class InputText implements LabelableComponent, FormComponent, Interactive

/**
* A text label that will appear on the clear button for screen readers.
*
* @deprecated – translations are now built-in, if you need to override a string, please use `messageOverrides`.
*/
@Prop() intlClear?: string;

/**
* Accessible name that will appear while loading.
*
* @default "Loading"
* @deprecated – translations are now built-in, if you need to override a string, please use `messageOverrides`.
*/
@Prop() intlLoading?: string = COMMON_TEXT.loading;
@Prop() intlLoading?: string;

/** When `true`, the icon will be flipped when the element direction is right-to-left (`"rtl"`). */
@Prop({ reflect: true }) iconFlipRtl = false;
Expand Down Expand Up @@ -171,6 +191,26 @@ export class InputText implements LabelableComponent, FormComponent, Interactive
/** The component's value. */
@Prop({ mutable: true }) value = "";

/**
* Made into a prop for testing purposes only
*
* @internal
*/
@Prop({ mutable: true }) messages: Messages;

/**
* Use this property to override individual strings used by the component.
*/
@Prop({ mutable: true }) messageOverrides: Partial<Messages>;

@Watch("intlClear")
@Watch("intlLoading")
@Watch("defaultMessages")
@Watch("messageOverrides")
onMessagesChange(): void {
/** referred in t9n util */
}

@Watch("value")
valueWatcher(newValue: string, previousValue: string): void {
if (!this.userChangedValue) {
Expand All @@ -190,7 +230,7 @@ export class InputText implements LabelableComponent, FormComponent, Interactive

//--------------------------------------------------------------------------
//
// Private Properties
// Private State/Properties
//
//--------------------------------------------------------------------------

Expand Down Expand Up @@ -222,13 +262,25 @@ export class InputText implements LabelableComponent, FormComponent, Interactive

private userChangedValue = false;

@State() effectiveLocale: string;

@Watch("effectiveLocale")
effectiveLocaleChange(): void {
updateMessages(this, this.effectiveLocale);
}

@State() defaultMessages: Messages;

//--------------------------------------------------------------------------
//
// Lifecycle
//
//--------------------------------------------------------------------------

connectedCallback(): void {
connectLocalized(this);
connectMessages(this);

this.scale = getElementProp(this.el, "scale", this.scale);
this.status = getElementProp(this.el, "status", this.status);
this.inlineEditableEl = this.el.closest("calcite-inline-editable");
Expand All @@ -248,12 +300,16 @@ export class InputText implements LabelableComponent, FormComponent, Interactive
disconnectedCallback(): void {
disconnectLabel(this);
disconnectForm(this);
disconnectLocalized(this);
disconnectMessages(this);

this.mutationObserver?.disconnect();
this.el.removeEventListener("calciteInternalHiddenInputChange", this.hiddenInputChangeHandler);
}

componentWillLoad(): void {
async componentWillLoad(): Promise<void> {
this.requestedIcon = setRequestedIcon({}, this.icon, "text");
await setUpMessages(this);
}

componentDidRender(): void {
Expand Down Expand Up @@ -496,13 +552,13 @@ export class InputText implements LabelableComponent, FormComponent, Interactive
const dir = getElementDir(this.el);
const loader = (
<div class={CSS.loader}>
<calcite-progress label={this.intlLoading} type="indeterminate" />
<calcite-progress label={this.messages.loading} type="indeterminate" />
</div>
);

const inputClearButton = (
<button
aria-label={this.intlClear || TEXT.clear}
aria-label={this.messages.clear}
class={CSS.clearButton}
disabled={this.disabled || this.readOnly}
onClick={this.clearInputTextValue}
Expand Down
5 changes: 4 additions & 1 deletion src/components/input/input.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
labelable,
reflects,
renders,
hidden
hidden,
t9n
} from "../../tests/commonTests";
import { html } from "../../../support/formatting";
import { letterKeys, numberKeys } from "../../utils/key";
Expand Down Expand Up @@ -1602,4 +1603,6 @@ describe("calcite-input", () => {
it("supports type=number", () =>
formAssociated("<calcite-input type='number'></calcite-input>", { testValue: 5, submitsOnEnter: true }));
});

it("supports translation", () => t9n("calcite-input"));
});
Loading