diff --git a/src/app/base/components/PrefixedInput/PrefixedInput.test.tsx b/src/app/base/components/PrefixedInput/PrefixedInput.test.tsx index b6984ac984..8a15ef6580 100644 --- a/src/app/base/components/PrefixedInput/PrefixedInput.test.tsx +++ b/src/app/base/components/PrefixedInput/PrefixedInput.test.tsx @@ -1,18 +1,24 @@ /* eslint-disable testing-library/no-node-access */ -import { render, screen } from "@testing-library/react"; +import { render, screen, waitFor } from "@testing-library/react"; import PrefixedInput from "./PrefixedInput"; -const { getComputedStyle } = window; - beforeAll(() => { - // getComputedStyle is not implemeneted in jsdom, so we need to do this. - window.getComputedStyle = (elt) => getComputedStyle(elt); + Element.prototype.getBoundingClientRect = vi.fn(() => ({ + width: 100, + height: 0, + x: 0, + y: 0, + top: 0, + right: 0, + bottom: 0, + left: 0, + toJSON: vi.fn(), + })); }); afterAll(() => { - // Reset to original implementation - window.getComputedStyle = getComputedStyle; + vi.restoreAllMocks(); }); it("renders without crashing", async () => { @@ -25,18 +31,19 @@ it("renders without crashing", async () => { ).toBeInTheDocument(); }); -it("sets the --immutable css variable to the provided immutable text", async () => { - const { rerender } = render( +it("displays the immutable text", async () => { + render( ); - rerender( + expect(screen.getByText("Some text")).toBeInTheDocument(); +}); + +it("adjusts input padding", async () => { + render( ); + const inputElement = screen.getByRole("textbox", { name: "Limited input" }); - // Direct node access is needed here to check the CSS variable - expect( - screen.getByRole("textbox", { name: "Limited input" }).parentElement - ?.parentElement - ).toHaveStyle(`--immutable: "Some text";`); + await waitFor(() => expect(inputElement).toHaveStyle("padding-left: 100px")); }); diff --git a/src/app/base/components/PrefixedInput/PrefixedInput.tsx b/src/app/base/components/PrefixedInput/PrefixedInput.tsx index 4454a43097..b391baf251 100644 --- a/src/app/base/components/PrefixedInput/PrefixedInput.tsx +++ b/src/app/base/components/PrefixedInput/PrefixedInput.tsx @@ -1,5 +1,4 @@ -import type { RefObject } from "react"; -import { useEffect, useRef } from "react"; +import { useLayoutEffect, useRef } from "react"; import type { InputProps } from "@canonical/react-components"; import { Input } from "@canonical/react-components"; @@ -9,45 +8,42 @@ export type PrefixedInputProps = Omit & { immutableText: string; }; -// TODO: Upstream to maas-react-components https://warthogs.atlassian.net/browse/MAASENG-3113 const PrefixedInput = ({ immutableText, ...props }: PrefixedInputProps) => { - const prefixedInputRef: RefObject = useRef(null); + const prefixTextRef = useRef(null); + const inputWrapperRef = useRef(null); - useEffect(() => { - const inputWrapper = prefixedInputRef.current?.firstElementChild; - if (inputWrapper) { - if (props.label) { - // CSS variable "--immutable" is the content of the :before element, which shows the immutable octets - // "--top" is the `top` property of the :before element, which is adjusted if there is a label to prevent overlap - inputWrapper.setAttribute( - "style", - `--top: 2.5rem; --immutable: "${immutableText}"` - ); - } else { - inputWrapper.setAttribute("style", `--immutable: "${immutableText}"`); - } - - const width = window.getComputedStyle(inputWrapper, ":before").width; + useLayoutEffect(() => { + const prefixElement = prefixTextRef.current; + const inputElement = inputWrapperRef.current?.querySelector("input"); + if (prefixElement && inputElement) { // Adjust the left padding of the input to be the same width as the immutable octets. // This displays the user input and the unchangeable text together as one IP address. - inputWrapper - .querySelector("input") - ?.setAttribute("style", `padding-left: ${width}`); + const prefixWidth = prefixElement.getBoundingClientRect().width; + inputElement.style.paddingLeft = `${prefixWidth}px`; } - }, [prefixedInputRef, immutableText, props.label]); + }, [immutableText, props.label]); return ( -
- +
+
+ {immutableText} +
+
+ +
); }; diff --git a/src/app/base/components/PrefixedInput/_index.scss b/src/app/base/components/PrefixedInput/_index.scss index aa57e83f3a..ab7969f9a4 100644 --- a/src/app/base/components/PrefixedInput/_index.scss +++ b/src/app/base/components/PrefixedInput/_index.scss @@ -2,15 +2,18 @@ .prefixed-input { position: relative; - &__wrapper::before { + .prefixed-input__text { position: absolute; pointer-events: none; padding-left: $spv--small; padding-bottom: calc(0.4rem - 1px); padding-top: calc(0.4rem - 1px); - // TODO: Investigate replacement for using these variables https://warthogs.atlassian.net/browse/MAASENG-3116 - content: var(--immutable, ""); - top: var(--top, "inherit"); + } + + &--with-label { + .prefixed-input__text { + top: 2.5rem; + } } } -} +} \ No newline at end of file diff --git a/src/app/base/components/PrefixedIpInput/PrefixedIpInput.test.tsx b/src/app/base/components/PrefixedIpInput/PrefixedIpInput.test.tsx index cc6c9e8024..41042b8f40 100644 --- a/src/app/base/components/PrefixedIpInput/PrefixedIpInput.test.tsx +++ b/src/app/base/components/PrefixedIpInput/PrefixedIpInput.test.tsx @@ -10,18 +10,6 @@ import PrefixedIpInput from "./PrefixedIpInput"; import { renderWithBrowserRouter } from "@/testing/utils"; -const { getComputedStyle } = window; - -beforeAll(() => { - // getComputedStyle is not implemeneted in jsdom, so we need to do this. - window.getComputedStyle = (elt) => getComputedStyle(elt); -}); - -afterAll(() => { - // Reset to original implementation - window.getComputedStyle = getComputedStyle; -}); - it("displays the correct range help text for a subnet", () => { render( @@ -31,20 +19,6 @@ it("displays the correct range help text for a subnet", () => { expect(screen.getByText("10.0.0.[1-254]")).toBeInTheDocument(); }); -it("sets the --immutable css variable to the immutable octets of the subnet", () => { - render( - - - - ); - - // Direct node access is needed here to check the CSS variable - expect( - screen.getByRole("textbox", { name: "IP address" }).parentElement - ?.parentElement - ).toHaveStyle(`--immutable: "10.0.0."`); -}); - it("displays the correct placeholder for a subnet", () => { render(