Skip to content

Commit

Permalink
Fix setValueOnMove state on Select not syncing between connected …
Browse files Browse the repository at this point in the history
…stores (#2858)

This PR fixes the
[`setValueOnMove`](https://ariakit.org/reference/use-select-store#setvalueonmove)
state on the [Select](https://ariakit.org/components/select) module not
syncing between multiple stores.

The following now works as expected:

```js
const store1 = useSelectStore();
const store2 = useSelectStore({ store: store1, setValueOnMove: true });

store1.useState("setValueOnMove") === store2.useState("setValueOnMove"); // true
```
  • Loading branch information
diegohaz committed Sep 21, 2023
1 parent a1ceed5 commit 662742f
Show file tree
Hide file tree
Showing 20 changed files with 208 additions and 758 deletions.
15 changes: 15 additions & 0 deletions .changeset/2858-select-sync-set-value-on-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@ariakit/react-core": patch
"@ariakit/react": patch
---

Fixed the [`setValueOnMove`](https://ariakit.org/reference/use-select-store#setvalueonmove) state on the [Select](https://ariakit.org/components/select) module not syncing between multiple stores.

The following now works as expected:

```js
const store1 = useSelectStore();
const store2 = useSelectStore({ store: store1, setValueOnMove: true });

store1.useState("setValueOnMove") === store2.useState("setValueOnMove"); // true
```
19 changes: 10 additions & 9 deletions examples/select-autofill/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import * as Ariakit from "@ariakit/react";
import "./style.css";
import * as Ariakit from "@ariakit/react";

export default function Example() {
const select = Ariakit.useSelectStore({ defaultValue: "Student" });
return (
<form className="wrapper">
<label htmlFor="email">Email</label>
<input type="email" id="email" name="email" className="input" />
<Ariakit.SelectLabel store={select}>Role</Ariakit.SelectLabel>
<Ariakit.Select store={select} name="role" className="button" />
<Ariakit.SelectPopover store={select} sameWidth className="popover">
<Ariakit.SelectItem className="select-item" value="Student" />
<Ariakit.SelectItem className="select-item" value="Tutor" />
<Ariakit.SelectItem className="select-item" value="Parent" />
</Ariakit.SelectPopover>
<Ariakit.SelectProvider defaultValue="Student">
<Ariakit.SelectLabel>Role</Ariakit.SelectLabel>
<Ariakit.Select name="role" className="button" />
<Ariakit.SelectPopover sameWidth className="popover">
<Ariakit.SelectItem className="select-item" value="Student" />
<Ariakit.SelectItem className="select-item" value="Tutor" />
<Ariakit.SelectItem className="select-item" value="Parent" />
</Ariakit.SelectPopover>
</Ariakit.SelectProvider>
</form>
);
}
51 changes: 51 additions & 0 deletions examples/select-combobox-store/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import "./style.css";
import { useDeferredValue, useMemo } from "react";
import * as Ariakit from "@ariakit/react";
import { matchSorter } from "match-sorter";
import list from "../select-combobox/list.js";

export default function Example() {
const combobox = Ariakit.useComboboxStore({ resetValueOnHide: true });
const select = Ariakit.useSelectStore({ combobox, defaultValue: "Apple" });

const value = combobox.useState("value");
const deferredValue = useDeferredValue(value);

const matches = useMemo(() => {
return matchSorter(list, deferredValue, {
baseSort: (a, b) => (a.index < b.index ? -1 : 1),
});
}, [deferredValue]);

return (
<div className="wrapper">
<Ariakit.SelectLabel store={select}>Favorite fruit</Ariakit.SelectLabel>
<Ariakit.Select store={select} className="button" />
<Ariakit.SelectPopover
store={select}
gutter={4}
sameWidth
className="popover"
>
<div className="combobox-wrapper">
<Ariakit.Combobox
store={combobox}
autoSelect
placeholder="Search..."
className="combobox"
/>
</div>
<Ariakit.ComboboxList store={combobox}>
{matches.map((value) => (
<Ariakit.ComboboxItem
key={value}
focusOnHover
className="select-item"
render={<Ariakit.SelectItem value={value} />}
/>
))}
</Ariakit.ComboboxList>
</Ariakit.SelectPopover>
</div>
);
}
1 change: 1 addition & 0 deletions examples/select-combobox-store/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import url("../select-combobox/style.css");
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ async function expectSelected(page: Page, name: string) {
}

test.beforeEach(async ({ page }) => {
await page.goto("/previews/select-combobox");
await page.goto("/previews/select-combobox-store");
});

test("auto select first option", async ({ page }) => {
Expand Down
1 change: 1 addition & 0 deletions examples/select-combobox-store/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "../select-combobox/test.js";
69 changes: 35 additions & 34 deletions examples/select-combobox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,52 @@
import "./style.css";
import { useDeferredValue, useMemo } from "react";
import { startTransition, useMemo, useState } from "react";
import * as Ariakit from "@ariakit/react";
import { matchSorter } from "match-sorter";
import list from "./list.js";

export default function Example() {
const combobox = Ariakit.useComboboxStore({ resetValueOnHide: true });
const select = Ariakit.useSelectStore({ combobox, defaultValue: "Apple" });

const value = combobox.useState("value");
const deferredValue = useDeferredValue(value);
const [searchValue, setSearchValue] = useState("");

const matches = useMemo(() => {
return matchSorter(list, deferredValue, {
return matchSorter(list, searchValue, {
baseSort: (a, b) => (a.index < b.index ? -1 : 1),
});
}, [deferredValue]);
}, [searchValue]);

return (
<div className="wrapper">
<Ariakit.SelectLabel store={select}>Favorite fruit</Ariakit.SelectLabel>
<Ariakit.Select store={select} className="button" />
<Ariakit.SelectPopover
store={select}
gutter={4}
sameWidth
className="popover"
<Ariakit.ComboboxProvider
resetValueOnHide
setValue={(value) => {
startTransition(() => {
setSearchValue(value);
});
}}
>
<div className="combobox-wrapper">
<Ariakit.Combobox
store={combobox}
autoSelect
placeholder="Search..."
className="combobox"
/>
</div>
<Ariakit.ComboboxList store={combobox}>
{matches.map((value) => (
<Ariakit.ComboboxItem
key={value}
focusOnHover
className="select-item"
render={<Ariakit.SelectItem value={value} />}
/>
))}
</Ariakit.ComboboxList>
</Ariakit.SelectPopover>
<Ariakit.SelectProvider defaultValue="Apple">
<Ariakit.SelectLabel>Favorite fruit</Ariakit.SelectLabel>
<Ariakit.Select className="button" />
<Ariakit.SelectPopover gutter={4} sameWidth className="popover">
<div className="combobox-wrapper">
<Ariakit.Combobox
autoSelect
placeholder="Search..."
className="combobox"
/>
</div>
<Ariakit.ComboboxList>
{matches.map((value) => (
<Ariakit.SelectItem
key={value}
value={value}
className="select-item"
render={<Ariakit.ComboboxItem />}
/>
))}
</Ariakit.ComboboxList>
</Ariakit.SelectPopover>
</Ariakit.SelectProvider>
</Ariakit.ComboboxProvider>
</div>
);
}
2 changes: 1 addition & 1 deletion examples/select-combobox/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ tags:

<div data-description>

Combining <a href="/components/select">Select</a> and <a href="/components/combobox">Combobox</a> to create a dropdown with a search field that can be used to filter items.
Combining [Select](/components/select) and [Combobox](/components/combobox) to create a dropdown with a search field that can be used to filter items.

</div>

Expand Down
60 changes: 60 additions & 0 deletions examples/select-grid-store/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as Ariakit from "@ariakit/react";
import Square from "../select-grid/square.jsx";
import "./style.css";

export default function Example() {
const select = Ariakit.useSelectStore({
defaultValue: "Center",
placement: "bottom",
setValueOnMove: true,
});
const value = select.useState("value");

const renderItem = (value: string) => (
<Ariakit.SelectItem
value={value}
className="select-item"
focusOnHover={(event) => {
// When the mouse leaves the item, we don't want to unset the active
// item.
if (event.type === "mouseleave") return false;
// By default, hovering over an item doesn't focus it, nor does it set
// the value. So we need to manually "move" to the item so it gets
// focused and the value is set.
select.move(event.currentTarget.id);
return true;
}}
>
<Ariakit.VisuallyHidden>{value}</Ariakit.VisuallyHidden>
</Ariakit.SelectItem>
);

return (
<div className="wrapper">
<Ariakit.SelectLabel store={select}>Position</Ariakit.SelectLabel>
<Ariakit.Select store={select} showOnKeyDown={false} className="button">
<Square value={value} />
{value}
<Ariakit.SelectArrow />
</Ariakit.Select>
<Ariakit.SelectPopover store={select} role="grid" className="popover">
<Ariakit.PopoverArrow className="arrow" />
<Ariakit.SelectRow className="row">
{renderItem("Top Left")}
{renderItem("Top Center")}
{renderItem("Top Right")}
</Ariakit.SelectRow>
<Ariakit.SelectRow className="row">
{renderItem("Center Left")}
{renderItem("Center")}
{renderItem("Center Right")}
</Ariakit.SelectRow>
<Ariakit.SelectRow className="row">
{renderItem("Bottom Left")}
{renderItem("Bottom Center")}
{renderItem("Bottom Right")}
</Ariakit.SelectRow>
</Ariakit.SelectPopover>
</div>
);
}
1 change: 1 addition & 0 deletions examples/select-grid-store/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import url("../select-grid/style.css");
1 change: 1 addition & 0 deletions examples/select-grid-store/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "../select-grid/test.js";
61 changes: 30 additions & 31 deletions examples/select-grid/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import "./style.css";
import { useState } from "react";
import * as Ariakit from "@ariakit/react";
import Square from "./square.jsx";
import "./style.css";

export default function Example() {
const select = Ariakit.useSelectStore({
defaultValue: "Center",
placement: "bottom",
setValueOnMove: true,
});
const value = select.useState("value");
const [value, setValue] = useState("Center");
const select = Ariakit.useSelectStore({ value, setValue });

const renderItem = (value: string) => (
<Ariakit.SelectItem
Expand All @@ -31,30 +28,32 @@ export default function Example() {

return (
<div className="wrapper">
<Ariakit.SelectLabel store={select}>Position</Ariakit.SelectLabel>
<Ariakit.Select store={select} showOnKeyDown={false} className="button">
<Square value={value} />
{value}
<Ariakit.SelectArrow />
</Ariakit.Select>
<Ariakit.SelectPopover store={select} role="grid" className="popover">
<Ariakit.PopoverArrow className="arrow" />
<Ariakit.SelectRow className="row">
{renderItem("Top Left")}
{renderItem("Top Center")}
{renderItem("Top Right")}
</Ariakit.SelectRow>
<Ariakit.SelectRow className="row">
{renderItem("Center Left")}
{renderItem("Center")}
{renderItem("Center Right")}
</Ariakit.SelectRow>
<Ariakit.SelectRow className="row">
{renderItem("Bottom Left")}
{renderItem("Bottom Center")}
{renderItem("Bottom Right")}
</Ariakit.SelectRow>
</Ariakit.SelectPopover>
<Ariakit.SelectProvider store={select} placement="bottom" setValueOnMove>
<Ariakit.SelectLabel>Position</Ariakit.SelectLabel>
<Ariakit.Select showOnKeyDown={false} className="button">
<Square value={value} />
{value}
<Ariakit.SelectArrow />
</Ariakit.Select>
<Ariakit.SelectPopover role="grid" className="popover">
<Ariakit.PopoverArrow className="arrow" />
<Ariakit.SelectRow className="row">
{renderItem("Top Left")}
{renderItem("Top Center")}
{renderItem("Top Right")}
</Ariakit.SelectRow>
<Ariakit.SelectRow className="row">
{renderItem("Center Left")}
{renderItem("Center")}
{renderItem("Center Right")}
</Ariakit.SelectRow>
<Ariakit.SelectRow className="row">
{renderItem("Bottom Left")}
{renderItem("Bottom Center")}
{renderItem("Bottom Right")}
</Ariakit.SelectRow>
</Ariakit.SelectPopover>
</Ariakit.SelectProvider>
</div>
);
}

1 comment on commit 662742f

@vercel
Copy link

@vercel vercel bot commented on 662742f Sep 21, 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:

ariakit – ./

ariakit-ariakit.vercel.app
ariakit-git-main-ariakit.vercel.app
www.ariakit.org
ariakit.org

Please sign in to comment.