Skip to content

Commit

Permalink
Textarea: translations (#3302)
Browse files Browse the repository at this point in the history
  • Loading branch information
HalvorHaugan authored Oct 30, 2024
1 parent 9757d7d commit 6e49de8
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 43 deletions.
4 changes: 1 addition & 3 deletions @navikt/core/react/src/form/form-progress/FormProgress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,7 @@ export const FormProgress = forwardRef<HTMLDivElement, FormProgressProps>(
<Collapsible lazy open={open} onOpenChange={onOpenChange}>
<HStack justify="space-between" align="center">
<BodyShort as="span">
{translate("step", {
replacements: { activeStep, totalSteps },
})}
{translate("step", { activeStep, totalSteps })}
</BodyShort>
<Collapsible.Trigger asChild aria-expanded={undefined}>
<Button
Expand Down
18 changes: 7 additions & 11 deletions @navikt/core/react/src/form/textarea/Textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,17 +180,13 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
{...(describedBy ? { "aria-describedby": describedBy } : {})}
/>
{hasMaxLength && !readOnly && !inputProps.disabled && (
<>
<span id={maxLengthId} className="navds-sr-only">
{`Tekstområde med plass til ${maxLength} tegn.`}
</span>
<Counter
maxLength={maxLength}
currentLength={props.value?.length ?? uncontrolledValue.length}
size={size}
i18n={i18n}
/>
</>
<Counter
maxLengthId={maxLengthId}
maxLength={maxLength}
currentLength={props.value?.length ?? uncontrolledValue.length}
size={size}
i18n={i18n}
/>
)}
<div
className="navds-form-field__error"
Expand Down
38 changes: 31 additions & 7 deletions @navikt/core/react/src/form/textarea/TextareaCounter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,32 @@ import cl from "clsx";
import React, { useEffect, useState } from "react";
import { BodyShort } from "../../typography";
import debounce from "../../util/debounce";
import { useI18n } from "../../util/i18n/i18n.context";
import type { TextareaProps } from "./Textarea";

interface Props {
maxLengthId: string;
maxLength: number;
currentLength: number;
size: TextareaProps["size"];
i18n: TextareaProps["i18n"];
}

const TextareaCounter = ({ maxLength, currentLength, size, i18n }: Props) => {
const difference = maxLength - currentLength;
const TextareaCounter = ({
maxLengthId,
maxLength,
currentLength,
size,
i18n,
}: Props) => {
const translate = useI18n("Textarea", {
charsLeft: i18n?.counterLeft ? `{chars} ${i18n.counterLeft}` : undefined,
charsTooMany: i18n?.counterTooMuch
? `{chars} ${i18n.counterTooMuch}`
: undefined,
});

const difference = maxLength - currentLength;
const [debouncedDiff, setDebouncedDiff] = useState(difference);

useEffect(() => {
Expand All @@ -29,12 +43,16 @@ const TextareaCounter = ({ maxLength, currentLength, size, i18n }: Props) => {

return (
<>
<span id={maxLengthId} className="navds-sr-only">
{translate("maxLength", { maxLength })}
</span>

{difference < 20 && (
<span
role="status"
className="navds-textarea__sr-counter navds-sr-only"
>
{getCounterText(debouncedDiff, i18n)}
{getCounterText(debouncedDiff, translate)}
</span>
)}

Expand All @@ -44,15 +62,21 @@ const TextareaCounter = ({ maxLength, currentLength, size, i18n }: Props) => {
})}
size={size}
>
{getCounterText(difference, i18n)}
{getCounterText(difference, translate)}
</BodyShort>
</>
);
};

const getCounterText = (difference: number, i18n: TextareaProps["i18n"]) =>
const getCounterText = (
difference: number,
translate: (
key: "charsTooMany" | "charsLeft",
replacements?: { chars: number },
) => string,
) =>
difference < 0
? `${Math.abs(difference)} ${i18n?.counterTooMuch ?? "tegn for mye"}`
: `${difference} ${i18n?.counterLeft ?? "tegn igjen"}`;
? translate("charsTooMany", { chars: Math.abs(difference) })
: translate("charsLeft", { chars: difference });

export default TextareaCounter;
7 changes: 1 addition & 6 deletions @navikt/core/react/src/form/textarea/textarea.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,11 @@ export default meta;

type Story = StoryObj<typeof Textarea>;

export const Default: StoryObj<typeof Textarea> = {
render: (props) => {
return <Textarea {...props} />;
},

export const Default: Story = {
args: {
maxLength: 0,
label: "Ipsum enim quis culpa",
},

argTypes: {
resize: {
control: { type: "radio" },
Expand Down
8 changes: 3 additions & 5 deletions @navikt/core/react/src/progress-bar/ProgressBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,11 @@ export const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(
aria-valuetext={
simulated?.seconds
? translate("progressUnknown", {
replacements: { seconds: Math.round(simulated?.seconds) },
seconds: Math.round(simulated?.seconds),
})
: translate("progress", {
replacements: {
current: Math.round(value),
max: Math.round(valueMax),
},
current: Math.round(value),
max: Math.round(valueMax),
})
}
// biome-ignore lint/a11y/useAriaPropsForRole: We found that adding valueMin was not needed
Expand Down
10 changes: 4 additions & 6 deletions @navikt/core/react/src/util/i18n/i18n.context.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,15 @@ describe("useI18n", () => {
};
const { result } = renderHook(() => useI18n("FileUpload", i18n));
const translate = result.current;
expect(
translate("item.uploading", { replacements: { name: "John", cnt: 3 } }),
).toBe("Hello, John. You have 3 messages.");
expect(translate("item.uploading", { name: "John", cnt: 3 })).toBe(
"Hello, John. You have 3 messages.",
);
});

test("should throw an error if replacement key is not found", () => {
const i18n = { item: { uploading: "Hello, {name}" } };
const { result } = renderHook(() => useI18n("FileUpload", i18n));
const translate = result.current;
expect(() =>
translate("item.uploading", { replacements: { other: "John" } }),
).toThrowError();
expect(() => translate("item.uploading", { other: "John" })).toThrowError();
});
});
10 changes: 5 additions & 5 deletions @navikt/core/react/src/util/i18n/i18n.context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function useI18n<T extends Component>(
*/
const translate = (
keypath: NestedKeyOf<Translations[T]>,
options?: { replacements: Record<string, string | number> },
replacements?: Record<string, string | number>,
) => {
const text = get(
keypath,
Expand All @@ -37,19 +37,19 @@ export function useI18n<T extends Component>(
: [i18n[componentName]]),
);

if (options?.replacements) {
if (replacements) {
return text.replace(REPLACE_REGEX, (match) => {
const replacement = match.substring(1, match.length - 1);

if (options.replacements[replacement] === undefined) {
const replacementData = JSON.stringify(options.replacements);
if (replacements[replacement] === undefined) {
const replacementData = JSON.stringify(replacements);

throw new Error(
`Error translating key '${keypath}'. No replacement syntax ({}) found for key '${replacement}'. The following replacements were passed: '${replacementData}'`,
);
}

return options.replacements[replacement] as string; // can also be a number, but JS doesn't mind...
return replacements[replacement] as string; // can also be a number, but JS doesn't mind...
});
}

Expand Down
5 changes: 5 additions & 0 deletions @navikt/core/react/src/util/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,9 @@ export default {
clear: "Clear",
search: "Search",
},
Textarea: {
maxLength: "Text area with a {maxLength} character limit.",
charsTooMany: "{chars} characters too many",
charsLeft: "{chars} characters left",
},
} satisfies Translations;
6 changes: 6 additions & 0 deletions @navikt/core/react/src/util/i18n/locales/nb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,10 @@ export default {
clear: "Tøm",
search: "Søk",
},
Textarea: {
/** Screen readers only */
maxLength: "Tekstområde med plass til {maxLength} tegn.",
charsTooMany: "{chars} tegn for mye",
charsLeft: "{chars} tegn igjen",
},
} satisfies TranslationMap;
5 changes: 5 additions & 0 deletions @navikt/core/react/src/util/i18n/locales/nn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,9 @@ export default {
clear: "Tøm",
search: "Søk",
},
Textarea: {
maxLength: "Tekstområde med plass til {maxLength} teikn.",
charsTooMany: "{chars} teikn for mykje",
charsLeft: "{chars} teikn igjen",
},
} satisfies Translations;

0 comments on commit 6e49de8

Please sign in to comment.