Skip to content

Commit

Permalink
chore: update tabs machine and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
segunadebayo committed Oct 14, 2021
1 parent 9189728 commit 008589c
Show file tree
Hide file tree
Showing 8 changed files with 392 additions and 176 deletions.
118 changes: 118 additions & 0 deletions cypress/integration/tabs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/* eslint-disable jest/expect-expect */
describe("tabs", () => {
beforeEach(() => {
cy.visit("/tabs")
cy.injectAxe()

cy.findByTestId("nils-tab").as("nilsTab")
cy.findByTestId("agnes-tab").as("agnesTab")
cy.findByTestId("joke-tab").as("jokeTab")

cy.findByTestId("nils-tab-panel").as("nilsPanel")
cy.findByTestId("agnes-tab-panel").as("agnesPanel")
cy.findByTestId("joke-tab-panel").as("jokePanel")
})

it("should have no accessibility violation", () => {
cy.checkA11y(".tabs")
})

describe("in automatic mode", () => {
it("should select the correct tab on click", () => {
cy.get("@nilsPanel").should("be.visible")
cy.get("@agnesTab").click()
cy.get("@agnesPanel").should("be.visible")
cy.get("@jokeTab").click()
cy.get("@jokePanel").should("be.visible")
})

it("on `ArrowRight`: should select & focus the next tab", () => {
cy.get("@nilsTab").focus().realType("{rightarrow}")
cy.get("@agnesTab").should("have.focus")
cy.get("@agnesPanel").should("be.visible")
cy.get("@agnesTab").realType("{rightarrow}")
cy.get("@jokeTab").should("have.focus")
cy.get("@jokePanel").should("be.visible")
cy.get("@jokeTab").realType("{rightarrow}")
// the keyboard navigation should circle back
cy.get("@nilsTab").should("have.focus")
cy.get("@nilsPanel").should("be.visible")
})

it("on `ArrowLeft`: should select & focus the previous tab", () => {
cy.get("@nilsTab").focus().realType("{leftarrow}")
cy.get("@jokeTab").should("have.focus")
cy.get("@jokePanel").should("be.visible")
cy.get("@jokeTab").realType("{leftarrow}")
cy.get("@agnesTab").should("have.focus")
cy.get("@agnesPanel").should("be.visible")
cy.get("@agnesTab").realType("{leftarrow}")
cy.get("@nilsTab").should("have.focus")
cy.get("@nilsPanel").should("be.visible")
})

it("on `Home` should select first tab", () => {
cy.get("@jokeTab").click().realType("{home}")
cy.get("@nilsTab").should("have.focus")
cy.get("@nilsPanel").should("be.visible")
})

it("on `End` should select last tab", () => {
cy.get("@nilsTab").focus().realType("{end}")
cy.get("@jokeTab").should("have.focus")
cy.get("@jokePanel").should("be.visible")
})
})

describe("in manual mode", () => {
beforeEach(() => {
cy.findByTestId("manual").check()
})

it("should have no accessibility violation", () => {
cy.checkA11y(".tabs")
})

it("on `ArrowRight`: should select & focus the next tab", () => {
cy.get("@nilsTab").focus().realType("{rightarrow}")
cy.get("@agnesTab").should("have.focus")
cy.get("@agnesPanel").should("not.be.visible")
cy.get("@agnesTab").realType("{enter}")
cy.get("@agnesPanel").should("be.visible")
cy.get("@agnesTab").realType("{rightarrow}")
cy.get("@jokeTab").should("have.focus")
cy.get("@jokePanel").should("not.be.visible")
cy.get("@jokeTab").realType("{enter}")
cy.get("@jokePanel").should("be.visible")
})

it("on `ArrowLeft`: should select & focus the previous tab", () => {
cy.get("@nilsTab").focus().realType("{leftarrow}")
cy.get("@jokeTab").should("have.focus")
cy.get("@jokePanel").should("not.be.visible")
cy.get("@jokeTab").realType("{enter}")
cy.get("@jokePanel").should("be.visible")
cy.get("@jokeTab").realType("{leftarrow}")
cy.get("@agnesTab").should("have.focus")
cy.get("@agnesPanel").should("not.be.visible")
cy.get("@agnesTab").realType("{enter}")
cy.get("@agnesPanel").should("be.visible")
})

it("on `Home`: should go to first tab", () => {
cy.get("@agnesTab").click().realType("{home}")
cy.get("@nilsTab").should("have.focus")
cy.get("@nilsPanel").should("not.be.visible")
cy.get("@nilsTab").realType("{enter}")
cy.get("@nilsPanel").should("be.visible")
})

it("on `End`: should go to last tab", () => {
cy.get("@nilsTab").click().realType("{end}")
cy.get("@jokeTab").should("have.focus")
cy.get("@jokePanel").should("not.be.visible")
cy.get("@jokeTab").realType("{enter}")
cy.get("@jokePanel").should("be.visible")
})
})
})
176 changes: 88 additions & 88 deletions examples/next-ts/hooks/use-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,101 +39,101 @@ export function useControls<T extends ControlRecord>(config: T) {

return {
context: state,
ui: (props: React.ComponentProps<"div">) => (
ui: () => (
<div
{...props}
style={{
display: "inline-flex",
gap: "24px",
display: "inline-block",
background: "lightgray",
padding: "12px",
borderRadius: "8px",
border: "1px solid lightgray",
background: "lightgray",
margin: "24px",
...props.style,
margin: "24px 0",
}}
>
{Object.keys(config).map((key) => {
const { type, label, options, placeholder, min, max } = (config[key] ?? {}) as any
switch (type) {
case "boolean":
return (
<div key={key}>
<input
data-testid={key}
id={label}
type="checkbox"
defaultChecked={state[key] as boolean}
onChange={(e) => {
setState((s) => ({ ...s, [key]: e.target.checked }))
}}
/>
<label htmlFor={label}>{label}</label>
</div>
)
case "string":
return (
<div key={key}>
<label style={{ marginRight: "10px" }}>{label}</label>
<input
data-testid={key}
type="text"
placeholder={placeholder}
defaultValue={state[key] as string}
onKeyDown={(e) => {
if (e.key === "Enter") {
setState((s) => ({ ...s, [key]: (e.target as HTMLInputElement).value }))
}
}}
/>
</div>
)
case "select":
return (
<div key={key}>
<label htmlFor={label} style={{ marginRight: "10px" }}>
{label}
</label>
<select
data-testid={key}
id={label}
defaultValue={state[key] as string}
onChange={(e) => {
setState((s) => ({ ...s, [key]: e.target.value }))
}}
>
<option>-----</option>
{options.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
</div>
)
case "number":
return (
<div key={key}>
<label htmlFor={label} style={{ marginRight: "10px" }}>
{label}
</label>
<input
data-testid={key}
id={label}
type="number"
min={min}
max={max}
defaultValue={state[key] as number}
onKeyDown={(e) => {
if (e.key === "Enter") {
setState((s) => ({ ...s, [key]: e.currentTarget?.valueAsNumber }))
}
}}
/>
</div>
)
}
})}
<p style={{ fontSize: "small", all: "unset", display: "block", marginBottom: "12px" }}>Property controls</p>
<div style={{ display: "inline-flex", gap: "24px" }}>
{Object.keys(config).map((key) => {
const { type, label, options, placeholder, min, max } = (config[key] ?? {}) as any
switch (type) {
case "boolean":
return (
<div key={key}>
<input
data-testid={key}
id={label}
type="checkbox"
defaultChecked={state[key] as boolean}
onChange={(e) => {
setState((s) => ({ ...s, [key]: e.target.checked }))
}}
/>
<label htmlFor={label}>{label}</label>
</div>
)
case "string":
return (
<div key={key}>
<label style={{ marginRight: "10px" }}>{label}</label>
<input
data-testid={key}
type="text"
placeholder={placeholder}
defaultValue={state[key] as string}
onKeyDown={(e) => {
if (e.key === "Enter") {
setState((s) => ({ ...s, [key]: (e.target as HTMLInputElement).value }))
}
}}
/>
</div>
)
case "select":
return (
<div key={key}>
<label htmlFor={label} style={{ marginRight: "10px" }}>
{label}
</label>
<select
data-testid={key}
id={label}
defaultValue={state[key] as string}
onChange={(e) => {
setState((s) => ({ ...s, [key]: e.target.value }))
}}
>
<option>-----</option>
{options.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
</div>
)
case "number":
return (
<div key={key}>
<label htmlFor={label} style={{ marginRight: "10px" }}>
{label}
</label>
<input
data-testid={key}
id={label}
type="number"
min={min}
max={max}
defaultValue={state[key] as number}
onKeyDown={(e) => {
if (e.key === "Enter") {
setState((s) => ({ ...s, [key]: e.currentTarget?.valueAsNumber }))
}
}}
/>
</div>
)
}
})}
</div>
</div>
),
}
Expand Down
Loading

0 comments on commit 008589c

Please sign in to comment.