Skip to content

Commit

Permalink
fix: input menu and text input (#153)
Browse files Browse the repository at this point in the history
- fix: remove text input label pointer events
- fix: return focus to text input control after selecting a menu option
- fix: handle more keyboard use cases with input menu
- test: simplify expect matcher and improve cross-browser support
  • Loading branch information
uipoet authored Jan 23, 2022
1 parent 56af62b commit cc44a01
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 98 deletions.
5 changes: 5 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jsx-a11y/recommended",
"plugin:playwright/playwright-test",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:prettier/recommended"
Expand All @@ -26,6 +27,10 @@
"import/newline-after-import": "warn",
"import/no-duplicates": "warn",
"import/order": "off",
"playwright/missing-playwright-await": [
"error",
{ "customMatchers": ["toHaveCSSOpacity", "toHavePseudoCSS"] }
],
"prettier/prettier": "warn",
"react/jsx-boolean-value": "warn",
"react/jsx-handler-names": "warn",
Expand Down
7 changes: 3 additions & 4 deletions global.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
declare namespace PlaywrightTest {
interface Matchers<R> {
toHaveCSSOpacity(expected: string): Promise<R>;
toHavePseudoCSS(
pseudoElement: "::before" | "::after",
toHaveStyle(
name: string,
expected: string
expected: string,
pseudoElement?: "::before" | "::after"
): Promise<R>;
}
}
39 changes: 31 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"eslint-config-prettier": "8.3.0",
"eslint-plugin-import": "2.25.4",
"eslint-plugin-jsx-a11y": "6.5.1",
"eslint-plugin-playwright": "0.7.1",
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-react": "7.28.0",
"eslint-plugin-react-hooks": "4.3.0",
Expand Down Expand Up @@ -113,6 +114,7 @@
"start:production": "vite build && vite preview",
"test": "playwright test",
"test:chrome": "playwright test --project='Desktop Chrome'",
"test:firefox": "playwright test --project='Desktop Firefox'",
"test:safari": "playwright test --project='Desktop Safari'",
"test:mobile:chrome": "playwright test --project='Mobile Chrome'",
"test:mobile:safari": "playwright test --project='Mobile Safari'"
Expand Down
88 changes: 63 additions & 25 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,84 @@ import {
type PseudoElement = "::before" | "::after";

expect.extend({
async toHaveCSSOpacity(locator: Locator, expected: string) {
const value = await locator.evaluate(
(element: HTMLElement | SVGElement) => getComputedStyle(element).opacity
);

const actual = value !== "0" ? Number(value).toFixed(2) : value;

if (actual === expected) return { pass: true };

return {
message: () => `actual: ${actual} expected: ${expected}`,
pass: false,
};
},
async toHavePseudoCSS(
async toHaveStyle(
locator: Locator,
pseudoElement: PseudoElement,
name: string,
expected: string
expected: string,
pseudoElement?: PseudoElement
) {
const value = await locator.evaluate(
const actual = await locator.evaluate(
(
element: HTMLElement | SVGElement,
{
pseudoElement,
expected,
name,
pseudoElement,
}: {
pseudoElement: PseudoElement;
expected: string;
name: string;
pseudoElement: PseudoElement;
}
) => {
const computedStyle = getComputedStyle(element, pseudoElement);

switch (name) {
case "borderColor":
if (computedStyle.borderColor) return computedStyle.borderColor;

// Firefox
if (
computedStyle.borderTopColor ===
computedStyle.borderBottomColor &&
computedStyle.borderTopColor === computedStyle.borderLeftColor &&
computedStyle.borderTopColor === computedStyle.borderRightColor
)
return computedStyle.borderTopColor;

break;
case "borderRadius":
if (computedStyle.borderRadius) return computedStyle.borderRadius;

// Firefox
if (
computedStyle.borderTopLeftRadius ===
computedStyle.borderTopRightRadius &&
computedStyle.borderTopLeftRadius ===
computedStyle.borderBottomLeftRadius &&
computedStyle.borderTopLeftRadius ===
computedStyle.borderBottomRightRadius
)
return computedStyle.borderTopLeftRadius;

break;
case "inset":
if (computedStyle.inset) return computedStyle.inset;

// Firefox
if (
computedStyle.top === computedStyle.bottom &&
computedStyle.top === computedStyle.left &&
computedStyle.top === computedStyle.right
)
return computedStyle.top;

break;
case "opacity":
// Safari
return Number(computedStyle.opacity).toFixed(
(expected.split(".")[1] || []).length
);
default:
return computedStyle[name];
}
) => getComputedStyle(element, pseudoElement)[name],
},
{
pseudoElement,
expected,
name,
pseudoElement,
}
);

const actual =
name === "opacity" && value !== "0" ? Number(value).toFixed(2) : value;

if (actual === expected) return { pass: true };

return {
Expand Down
2 changes: 1 addition & 1 deletion src/docs/pages/examples/input-menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function InputMenuExamples() {
flex={flex}
helper={helper ? "Helper message" : undefined}
id="input-menu-example"
label="Filled"
label="Label"
leadingIcon={leadingIcon && <MdFavorite />}
onChange={(value) => console.info("InputMenu", { value })}
options={options}
Expand Down
62 changes: 28 additions & 34 deletions src/lib/components/button/button.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,27 @@ test.describe("Button", () => {
await expect(button).toHaveCSS("backgroundColor", colors.primary);
await expect(button).toHaveCSS("color", colors.surface);
await expect(button).toHaveCSS("display", "flex");
await expect(button).toHavePseudoCSS("::before", "opacity", "0");
await expect(button).toHavePseudoCSS("::before", "position", "absolute");
await expect(button).toHaveStyle("borderRadius", "20px", "::before");
await expect(button).toHaveStyle("height", "40px", "::before");
await expect(button).toHaveStyle("inset", "0px", "::before");
await expect(button).toHaveStyle("opacity", "0", "::before");
await expect(button).toHaveStyle("position", "absolute", "::before");
});

test("hover", async ({ page }) => {
const button = page.locator("id=button");

await button.hover();

await expect(button).toHavePseudoCSS("::before", "opacity", "0.08");
await expect(button).toHaveStyle("opacity", "0.08", "::before");
});

test("focus", async ({ page }) => {
const button = page.locator("id=button");

await button.focus();

await expect(button).toHavePseudoCSS("::before", "opacity", "0.12");
await expect(button).toHaveStyle("opacity", "0.12", "::before");
});

test("disabled", async ({ colors, page }) => {
Expand All @@ -39,12 +42,12 @@ test.describe("Button", () => {
await page.check("id=button-disabled-checkbox");

await expect(button).toHaveCSS("color", colors.onSurface);
await expect(button).toHaveCSSOpacity("0.38");
await expect(button).toHavePseudoCSS("::before", "opacity", "0.12");
await expect(button).toHavePseudoCSS(
"::before",
await expect(button).toHaveStyle("opacity", "0.38");
await expect(button).toHaveStyle("opacity", "0.12", "::before");
await expect(button).toHaveStyle(
"backgroundColor",
colors.onSurface
colors.onSurface,
"::before"
);
});
});
Expand All @@ -61,10 +64,10 @@ test.describe("Button", () => {
`rgba(0, 0, 0, 0.2) 0px 1px 2px 1px`
);
await expect(button).toHaveCSS("color", colors.primary);
await expect(button).toHavePseudoCSS(
"::before",
await expect(button).toHaveStyle(
"backgroundColor",
colors.primary
colors.primary,
"::before"
);
});
});
Expand All @@ -81,10 +84,10 @@ test.describe("Button", () => {
colors.primaryContainer
);
await expect(button).toHaveCSS("color", colors.onPrimaryContainer);
await expect(button).toHavePseudoCSS(
"::before",
await expect(button).toHaveStyle(
"backgroundColor",
colors.onPrimaryContainer
colors.onPrimaryContainer,
"::before"
);
});
});
Expand All @@ -95,22 +98,20 @@ test.describe("Button", () => {
await page.click("id=button-appearance-menu >> button[value=outlined]");
});

test("default", async ({ browserName, colors, page }) => {
test("default", async ({ colors, page }) => {
const button = page.locator("id=button");

await expect(button).toHaveCSS("color", colors.primary);

if (browserName === "firefox") return;

await expect(button).toHavePseudoCSS(
"::after",
await expect(button).toHaveStyle(
"borderColor",
colors.outline
colors.outline,
"::after"
);
await expect(button).toHavePseudoCSS("::after", "borderRadius", "20px");
await expect(button).toHavePseudoCSS("::after", "height", "40px");
await expect(button).toHavePseudoCSS("::after", "inset", "0px");
await expect(button).toHavePseudoCSS("::after", "position", "absolute");
await expect(button).toHaveStyle("borderRadius", "20px", "::after");
await expect(button).toHaveStyle("height", "40px", "::after");
await expect(button).toHaveStyle("inset", "0px", "::after");
await expect(button).toHaveStyle("position", "absolute", "::after");
});

test("disabled", async ({ colors, page }) => {
Expand All @@ -119,25 +120,18 @@ test.describe("Button", () => {
await page.click("id=button-disabled-checkbox");

await expect(button).toHaveCSS("color", colors.onSurface);
await expect(button).toHaveCSSOpacity("0.38");
await expect(button).toHaveStyle("opacity", "0.38");
});
});

test.describe("text appearance", () => {
test("default", async ({ browserName, colors, page }) => {
test("default", async ({ colors, page }) => {
const button = page.locator("id=button");

await page.click("id=button-appearance");
await page.click("id=button-appearance-menu >> button[value=text]");

await expect(button).toHaveCSS("color", colors.primary);

if (browserName === "firefox") return;

await expect(button).toHavePseudoCSS("::before", "borderRadius", "20px");
await expect(button).toHavePseudoCSS("::before", "height", "40px");
await expect(button).toHavePseudoCSS("::before", "inset", "0px");
await expect(button).toHavePseudoCSS("::before", "position", "absolute");
});
});
});
Loading

0 comments on commit cc44a01

Please sign in to comment.