Skip to content

Commit

Permalink
refactor: ensure consistent *item naming convention (#882)
Browse files Browse the repository at this point in the history
  • Loading branch information
segunadebayo authored Sep 19, 2023
1 parent a415b6d commit fd71ad9
Show file tree
Hide file tree
Showing 96 changed files with 583 additions and 404 deletions.
40 changes: 40 additions & 0 deletions .changeset/happy-starfishes-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
"@zag-js/rating-group": minor
"@zag-js/toggle-group": minor
"@zag-js/radio-group": minor
"@zag-js/tags-input": minor
"@zag-js/accordion": minor
"@zag-js/checkbox": minor
"@zag-js/popover": minor
"@zag-js/select": minor
"@zag-js/menu": minor
---

- Refactor component anatomy to use consistent naming convention across all machines.

- **Accordion**

- `getTriggerProps` => `getItemTriggerProps`
- `getContentProps` => `getItemContentProps`

- **Radio**

- `getRadioProps` => `getItemProps`
- `getRadioControlProps` => `getItemControlProps`
- `getRadioLabelProps` => `getItemTextProps`
- `getRatingState` => `getItemState`
- `getRatingProps` => `getItemProps`

- **TagsInput**

- `getTagProps` => `getItemProps`
- `getTagDeleteTriggerProps` => `getItemDeleteTriggerProps`
- `getTagInputProps` => `getItemInputProps`

- **Toggle Group**
- `getToggleProps` => `getItemProps`

- **ToggleGroup**: Allow deselecting item when `multiple` is `false`.

- Add indicator part to some components for ease of styling. Added `AccordionItemIndicator`, `SelectIndicator`,
`MenuIndicator`, `PopoverIndicator`
2 changes: 1 addition & 1 deletion e2e/rating-group.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { a11y, controls as testControls, part, testid } from "./__utils"

const control = part("control")
const hiddenInput = testid("hidden-input")
const rating = part("rating")
const rating = part("item")

const getRating = (page: Page, value: number) => {
return page.locator(rating).nth(value - 1)
Expand Down
2 changes: 1 addition & 1 deletion e2e/tags-input.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ test.describe("tags-input", () => {
await page.keyboard.press("ArrowLeft")
await page.click("body", { force: true })

expect(await page.locator("[data-part=tag][data-selected]").count()).toBe(0)
expect(await page.locator("[data-part=item][data-selected]").count()).toBe(0)
})

test("removes tag on close button click", async ({ page }) => {
Expand Down
88 changes: 88 additions & 0 deletions e2e/toggle-group.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { expect, type Page, test, type Locator } from "@playwright/test"
import { a11y, controls, part } from "./__utils"

class PageModel {
constructor(public readonly page: Page) {}
get bold() {
return this.page.locator(part("item")).nth(0)
}
get italic() {
return this.page.locator(part("item")).nth(1)
}
get underline() {
return this.page.locator(part("item")).nth(2)
}
expectToBeFocused(locator: Locator) {
return expect(locator).toHaveAttribute("data-highlighted", "")
}
expectNotToBeFocused(locator: Locator) {
return expect(locator).not.toHaveAttribute("data-highlighted", "")
}
expectToBeSelected(locator: Locator) {
return expect(locator).toHaveAttribute("data-state", "on")
}
expectNotToBeSelected(locator: Locator) {
return expect(locator).not.toHaveAttribute("data-state", "on")
}
setMultiple() {
return controls(this.page).bool("multiple")
}
}

test.describe("toggle-group", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/toggle-group")
})

test("should have no accessibility violation", async ({ page }) => {
await a11y(page)
})

test("[single] should select on click", async ({ page }) => {
const screen = new PageModel(page)

await screen.bold.click()
await screen.expectToBeSelected(screen.bold)

await screen.italic.click()
await screen.expectToBeSelected(screen.italic)
await screen.expectNotToBeSelected(screen.bold)
})

test("[single] should select and deselect", async ({ page }) => {
const screen = new PageModel(page)

await screen.bold.click()
await screen.expectToBeSelected(screen.bold)

await screen.bold.click()
await screen.expectNotToBeSelected(screen.bold)
})

test("[multiple] should select multiple", async ({ page }) => {
const screen = new PageModel(page)
await screen.setMultiple()

await screen.bold.click()
await screen.italic.click()

await screen.expectToBeSelected(screen.bold)
await screen.expectToBeSelected(screen.italic)
})

test("[keyboard] when no toggle is selected, focus first toggle", async ({ page }) => {
const screen = new PageModel(page)

// focus on outside button
const outsideButton = page.getByRole("button", { name: "Outside" })
await outsideButton.focus()
await page.keyboard.press("Tab")

await expect(screen.bold).toBeFocused()

// shift tab back to outside button
await page.keyboard.press("Shift+Tab")
await expect(screen.bold).not.toBeFocused()
await expect(outsideButton).toBeFocused()
})
})
4 changes: 2 additions & 2 deletions examples/next-ts/pages/accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ export default function Page() {
{accordionData.map((item) => (
<div key={item.id} {...api.getItemProps({ value: item.id })}>
<h3>
<button data-testid={`${item.id}:trigger`} {...api.getTriggerProps({ value: item.id })}>
<button data-testid={`${item.id}:trigger`} {...api.getItemTriggerProps({ value: item.id })}>
{item.label}
</button>
</h3>
<div data-testid={`${item.id}:content`} {...api.getContentProps({ value: item.id })}>
<div data-testid={`${item.id}:content`} {...api.getItemContentProps({ value: item.id })}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</div>
Expand Down
8 changes: 4 additions & 4 deletions examples/next-ts/pages/radio-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ export default function Page() {
<div {...api.rootProps}>
<h3 {...api.labelProps}>Fruits</h3>
{radioData.map((opt) => (
<label key={opt.id} data-testid={`radio-${opt.id}`} {...api.getRadioProps({ value: opt.id })}>
<div data-testid={`control-${opt.id}`} {...api.getRadioControlProps({ value: opt.id })} />
<span data-testid={`label-${opt.id}`} {...api.getRadioLabelProps({ value: opt.id })}>
<label key={opt.id} data-testid={`radio-${opt.id}`} {...api.getItemProps({ value: opt.id })}>
<div data-testid={`control-${opt.id}`} {...api.getItemControlProps({ value: opt.id })} />
<span data-testid={`label-${opt.id}`} {...api.getItemTextProps({ value: opt.id })}>
{opt.label}
</span>
<input data-testid={`input-${opt.id}`} {...api.getRadioHiddenInputProps({ value: opt.id })} />
<input data-testid={`input-${opt.id}`} {...api.getItemHiddenInputProps({ value: opt.id })} />
</label>
))}
</div>
Expand Down
4 changes: 2 additions & 2 deletions examples/next-ts/pages/rating-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ export default function Page() {
<label {...api.labelProps}>Rate:</label>
<div {...api.controlProps}>
{api.sizeArray.map((index) => {
const state = api.getRatingState({ index })
const state = api.getItemState({ index })
return (
<span key={index} {...api.getRatingProps({ index })}>
<span key={index} {...api.getItemProps({ index })}>
{state.isHalf ? <HalfStar /> : <Star />}
</span>
)
Expand Down
6 changes: 3 additions & 3 deletions examples/next-ts/pages/segment-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ export default function Page() {
<div {...api.rootProps}>
<div {...api.indicatorProps} />
{radioData.map((opt) => (
<label key={opt.id} data-testid={`radio-${opt.id}`} {...api.getRadioProps({ value: opt.id })}>
<span data-testid={`label-${opt.id}`} {...api.getRadioLabelProps({ value: opt.id })}>
<label key={opt.id} data-testid={`radio-${opt.id}`} {...api.getItemProps({ value: opt.id })}>
<span data-testid={`label-${opt.id}`} {...api.getItemTextProps({ value: opt.id })}>
{opt.label}
</span>
<input data-testid={`input-${opt.id}`} {...api.getRadioHiddenInputProps({ value: opt.id })} />
<input data-testid={`input-${opt.id}`} {...api.getItemHiddenInputProps({ value: opt.id })} />
</label>
))}
</div>
Expand Down
6 changes: 3 additions & 3 deletions examples/next-ts/pages/tags-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ export default function Page() {
<div {...api.controlProps}>
{api.value.map((value, index) => (
<span key={`${toDashCase(value)}-tag-${index}`}>
<div data-testid={`${toDashCase(value)}-tag`} {...api.getTagProps({ index, value })}>
<div data-testid={`${toDashCase(value)}-tag`} {...api.getItemProps({ index, value })}>
<span data-testid={`${toDashCase(value)}-valuetext`}>{value} </span>
<button
data-testid={`${toDashCase(value)}-close-button`}
{...api.getTagDeleteTriggerProps({ index, value })}
{...api.getItemDeleteTriggerProps({ index, value })}
>
&#x2715;
</button>
</div>
<input data-testid={`${toDashCase(value)}-input`} {...api.getTagInputProps({ index, value })} />
<input data-testid={`${toDashCase(value)}-input`} {...api.getItemInputProps({ index, value })} />
</span>
))}
<input data-testid="input" placeholder="add tag" {...api.inputProps} />
Expand Down
4 changes: 2 additions & 2 deletions examples/next-ts/pages/toggle-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ export default function Page() {
<button>Outside</button>
<div {...api.rootProps}>
{toggleGroupData.map((item) => (
<button key={item.value} {...api.getToggleProps({ value: item.value })}>
<button key={item.value} {...api.getItemProps({ value: item.value })}>
{item.label}
</button>
))}
</div>
</main>

<Toolbar controls={controls.ui} viz>
<Toolbar controls={controls.ui}>
<StateVisualizer state={state} />
</Toolbar>
</>
Expand Down
16 changes: 16 additions & 0 deletions examples/nuxt-ts/components/HalfStar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<svg viewBox="0 0 273 260" data-part="star">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M135.977 214.086L52.1294 259.594L69.6031 165.229L0 99.1561L95.1465 86.614L135.977 1.04785V214.086Z"
fill="currentColor"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M135.977 213.039L219.826 258.546L202.352 164.181L271.957 98.1082L176.808 85.5661L135.977 0V213.039Z"
fill="#bdbdbd"
/>
</svg>
</template>
8 changes: 8 additions & 0 deletions examples/nuxt-ts/components/Star.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<template>
<svg viewBox="0 0 273 260" data-part="star">
<path
d="M136.5 0L177.83 86.614L272.977 99.1561L203.374 165.229L220.847 259.594L136.5 213.815L52.1528 259.594L69.6265 165.229L0.0233917 99.1561L95.1699 86.614L136.5 0Z"
fill="currentColor"
/>
</svg>
</template>
4 changes: 2 additions & 2 deletions examples/nuxt-ts/pages/accordion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ const api = computed(() => accordion.connect(state.value, send, normalizeProps))
<div v-bind="api.rootProps">
<div v-for="item in accordionData" v-bind="api.getItemProps({ value: item.id })">
<h3>
<button :data-testid="`${item.id}:trigger`" v-bind="api.getTriggerProps({ value: item.id })">
<button :data-testid="`${item.id}:trigger`" v-bind="api.getItemTriggerProps({ value: item.id })">
{{ item.label }}
</button>
</h3>
<div :data-testid="`${item.id}:content`" v-bind="api.getContentProps({ value: item.id })">
<div :data-testid="`${item.id}:content`" v-bind="api.getItemContentProps({ value: item.id })">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua.
</div>
Expand Down
8 changes: 4 additions & 4 deletions examples/nuxt-ts/pages/radio-group.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ const api = computed(() => radio.connect(state.value, send, normalizeProps))
v-for="opt in radioData"
:key="opt.id"
:data-testid="`radio-${opt.id}`"
v-bind="api.getRadioProps({ value: opt.id })"
v-bind="api.getItemProps({ value: opt.id })"
>
<div :data-testid="`control-${opt.id}`" v-bind="api.getRadioControlProps({ value: opt.id })" />
<span :data-testid="`label-${opt.id}`" v-bind="api.getRadioLabelProps({ value: opt.id })">
<div :data-testid="`control-${opt.id}`" v-bind="api.getItemControlProps({ value: opt.id })" />
<span :data-testid="`label-${opt.id}`" v-bind="api.getItemTextProps({ value: opt.id })">
{{ opt.label }}
</span>
<input :data-testid="`input-${opt.id}`" v-bind="api.getRadioHiddenInputProps({ value: opt.id })" />
<input :data-testid="`input-${opt.id}`" v-bind="api.getItemHiddenInputProps({ value: opt.id })" />
</label>
</div>

Expand Down
42 changes: 3 additions & 39 deletions examples/nuxt-ts/pages/rating-group.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<script setup lang="tsx">
<script setup lang="ts">
import * as rating from "@zag-js/rating-group"
import { ratingControls } from "@zag-js/shared"
import { normalizeProps, useMachine } from "@zag-js/vue"
Expand All @@ -17,52 +17,16 @@ const api = computed(() => rating.connect(state.value, send, normalizeProps))
const items = computed(() =>
api.value.sizeArray.map((index) => {
return [index, api.value.getRatingState({ index })] as const
return [index, api.value.getItemState({ index })] as const
}),
)
const HalfStar = defineComponent({
name: "HalfStar",
setup() {
return () => (
<svg viewBox="0 0 273 260" data-part="star">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M135.977 214.086L52.1294 259.594L69.6031 165.229L0 99.1561L95.1465 86.614L135.977 1.04785V214.086Z"
fill="currentColor"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M135.977 213.039L219.826 258.546L202.352 164.181L271.957 98.1082L176.808 85.5661L135.977 0V213.039Z"
fill="#bdbdbd"
/>
</svg>
)
},
})
const Star = defineComponent({
name: "Star",
setup() {
return () => (
<svg viewBox="0 0 273 260" data-part="star">
<path
d="M136.5 0L177.83 86.614L272.977 99.1561L203.374 165.229L220.847 259.594L136.5 213.815L52.1528 259.594L69.6265 165.229L0.0233917 99.1561L95.1699 86.614L136.5 0Z"
fill="currentColor"
/>
</svg>
)
},
})
</script>

<template>
<main class="rating">
<div v-bind="api.rootProps">
<div v-bind="api.controlProps">
<span v-for="[index, state] in items" :key="index" v-bind="api.getRatingProps({ index })">
<span v-for="[index, state] in items" :key="index" v-bind="api.getItemProps({ index })">
<HalfStar v-if="state.isHalf" />
<Star v-else="" />
</span>
Expand Down
6 changes: 3 additions & 3 deletions examples/nuxt-ts/pages/segment-control.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ const api = computed(() => radio.connect(state.value, send, normalizeProps))
v-for="opt in radioData"
:key="opt.id"
:data-testid="`radio-${opt.id}`"
v-bind="api.getRadioProps({ value: opt.id })"
v-bind="api.getItemProps({ value: opt.id })"
>
<span :data-testid="`label-${opt.id}`" v-bind="api.getRadioLabelProps({ value: opt.id })">
<span :data-testid="`label-${opt.id}`" v-bind="api.getItemTextProps({ value: opt.id })">
{{ opt.label }}
</span>
<input :data-testid="`input-${opt.id}`" v-bind="api.getRadioHiddenInputProps({ value: opt.id })" />
<input :data-testid="`input-${opt.id}`" v-bind="api.getItemHiddenInputProps({ value: opt.id })" />
</label>
</div>

Expand Down
6 changes: 3 additions & 3 deletions examples/nuxt-ts/pages/tags-input.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ const api = computed(() => tagsInput.connect(state.value, send, normalizeProps))
<label v-bind="api.labelProps">Enter frameworks:</label>
<div v-bind="api.controlProps">
<span v-for="(value, index) in api.value" :key="`${toDashCase(value)}-tag-${index}`">
<div :data-testid="`${toDashCase(value)}-tag`" v-bind="api.getTagProps({ index, value })">
<div :data-testid="`${toDashCase(value)}-tag`" v-bind="api.getItemProps({ index, value })">
<span>{{ value }} &nbsp;</span>
<button
:data-testid="`${toDashCase(value)}-close-button`"
v-bind="api.getTagDeleteTriggerProps({ index, value })"
v-bind="api.getItemDeleteTriggerProps({ index, value })"
>
&#x2715;
</button>
</div>
<input :data-testid="`${toDashCase(value)}-input`" v-bind="api.getTagInputProps({ index, value })" />
<input :data-testid="`${toDashCase(value)}-input`" v-bind="api.getItemInputProps({ index, value })" />
</span>

<input data-testid="input" placeholder="add tag" v-bind="api.inputProps" />
Expand Down
Loading

4 comments on commit fd71ad9

@vercel
Copy link

@vercel vercel bot commented on fd71ad9 Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on fd71ad9 Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

zag-solid – ./examples/solid-ts

zag-solid.vercel.app
zag-solid-chakra-ui.vercel.app
zag-solid-git-main-chakra-ui.vercel.app

@vercel
Copy link

@vercel vercel bot commented on fd71ad9 Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

zag-nextjs – ./examples/next-ts

zag-two.vercel.app
zag-nextjs-git-main-chakra-ui.vercel.app
zag-nextjs-chakra-ui.vercel.app

@vercel
Copy link

@vercel vercel bot commented on fd71ad9 Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

zag-vue – ./examples/vue-ts

zag-vue-git-main-chakra-ui.vercel.app
zag-vue.vercel.app
zag-vue-chakra-ui.vercel.app

Please sign in to comment.