@butternutbox/pawprint-native 1.6.1 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +22 -0
- package/dist/index.cjs +19 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +19 -9
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/molecules/SelectField/SelectField.test.tsx +42 -12
- package/src/components/molecules/SelectField/SelectField.tsx +33 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@butternutbox/pawprint-native",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "ButternutBox Pawprint Design System - React Native Components",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"test": "vitest run"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@butternutbox/pawprint-fonts": "^2.0.
|
|
26
|
-
"@butternutbox/pawprint-tokens": "^0.
|
|
25
|
+
"@butternutbox/pawprint-fonts": "^2.0.1",
|
|
26
|
+
"@butternutbox/pawprint-tokens": "^0.6.0",
|
|
27
27
|
"@emotion/native": "^11.11.0",
|
|
28
28
|
"@emotion/react": "^11.14.0",
|
|
29
29
|
"@rn-primitives/avatar": "^1.4.0",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from "react"
|
|
2
2
|
import { View } from "react-native"
|
|
3
3
|
import { screen, fireEvent, act, waitFor } from "@testing-library/react"
|
|
4
|
-
import { describe, it, expect, vi } from "vitest"
|
|
5
|
-
import {
|
|
4
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"
|
|
5
|
+
import { MEASURED_TRIGGER } from "../../../__mocks__/react-native"
|
|
6
6
|
import { renderWithTheme } from "../../../test-utils"
|
|
7
7
|
import { SelectField } from "./SelectField"
|
|
8
8
|
|
|
@@ -698,7 +698,18 @@ describe("SelectField", () => {
|
|
|
698
698
|
expect(item).toBeInTheDocument()
|
|
699
699
|
})
|
|
700
700
|
})
|
|
701
|
-
describe("when the
|
|
701
|
+
describe("when the field moves while the dropdown is open", () => {
|
|
702
|
+
const restingPageY = MEASURED_TRIGGER.pageY
|
|
703
|
+
|
|
704
|
+
beforeEach(() => {
|
|
705
|
+
vi.useFakeTimers()
|
|
706
|
+
})
|
|
707
|
+
|
|
708
|
+
afterEach(() => {
|
|
709
|
+
MEASURED_TRIGGER.pageY = restingPageY
|
|
710
|
+
vi.useRealTimers()
|
|
711
|
+
})
|
|
712
|
+
|
|
702
713
|
const renderSearchable = () =>
|
|
703
714
|
renderWithTheme(
|
|
704
715
|
<SelectField label="Breed" placeholder="Select a breed" searchable>
|
|
@@ -712,34 +723,53 @@ describe("SelectField", () => {
|
|
|
712
723
|
.querySelector("[data-trigger-position]")
|
|
713
724
|
?.getAttribute("data-trigger-position")
|
|
714
725
|
|
|
715
|
-
|
|
726
|
+
const advanceFrames = (count: number) =>
|
|
727
|
+
act(() => vi.advanceTimersByTime(count * 16))
|
|
728
|
+
|
|
729
|
+
it("anchors the dropdown to the field once it has opened", () => {
|
|
716
730
|
const { container } = renderSearchable()
|
|
717
731
|
|
|
718
732
|
fireEvent.click(screen.getAllByRole("button")[0])
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
act(() => Keyboard.emit("keyboardDidShow"))
|
|
733
|
+
advanceFrames(3)
|
|
722
734
|
|
|
723
735
|
expect(triggerPositionOf(container)).toBe(
|
|
724
736
|
JSON.stringify(MEASURED_TRIGGER)
|
|
725
737
|
)
|
|
726
738
|
})
|
|
727
739
|
|
|
728
|
-
it("
|
|
740
|
+
it("follows the field to where it comes to rest", () => {
|
|
729
741
|
const { container } = renderSearchable()
|
|
730
742
|
|
|
731
743
|
fireEvent.click(screen.getAllByRole("button")[0])
|
|
732
|
-
|
|
744
|
+
advanceFrames(3)
|
|
745
|
+
MEASURED_TRIGGER.pageY = 380
|
|
746
|
+
advanceFrames(3)
|
|
733
747
|
|
|
734
748
|
expect(triggerPositionOf(container)).toBe(
|
|
735
|
-
JSON.stringify(MEASURED_TRIGGER)
|
|
749
|
+
JSON.stringify({ ...MEASURED_TRIGGER, pageY: 380 })
|
|
750
|
+
)
|
|
751
|
+
})
|
|
752
|
+
|
|
753
|
+
it("holds the dropdown in place while the field is still moving", () => {
|
|
754
|
+
const { container } = renderSearchable()
|
|
755
|
+
|
|
756
|
+
fireEvent.click(screen.getAllByRole("button")[0])
|
|
757
|
+
advanceFrames(3)
|
|
758
|
+
|
|
759
|
+
for (const pageY of [650, 600, 550]) {
|
|
760
|
+
MEASURED_TRIGGER.pageY = pageY
|
|
761
|
+
advanceFrames(1)
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
expect(triggerPositionOf(container)).toBe(
|
|
765
|
+
JSON.stringify({ ...MEASURED_TRIGGER, pageY: restingPageY })
|
|
736
766
|
)
|
|
737
767
|
})
|
|
738
768
|
|
|
739
|
-
it("does not
|
|
769
|
+
it("does not track the field while the dropdown is closed", () => {
|
|
740
770
|
const { container } = renderSearchable()
|
|
741
771
|
|
|
742
|
-
|
|
772
|
+
advanceFrames(3)
|
|
743
773
|
|
|
744
774
|
expect(triggerPositionOf(container)).toBeUndefined()
|
|
745
775
|
})
|
|
@@ -80,25 +80,49 @@ type SelectFieldAnchorProps = {
|
|
|
80
80
|
enabled: boolean
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
type TriggerPosition = NonNullable<
|
|
84
|
+
ReturnType<typeof SelectPrimitive.useRootContext>["triggerPosition"]
|
|
85
|
+
>
|
|
86
|
+
|
|
87
|
+
const isSamePosition = (a: TriggerPosition | null, b: TriggerPosition | null) =>
|
|
88
|
+
a !== null &&
|
|
89
|
+
b !== null &&
|
|
90
|
+
a.width === b.width &&
|
|
91
|
+
a.height === b.height &&
|
|
92
|
+
a.pageX === b.pageX &&
|
|
93
|
+
a.pageY === b.pageY
|
|
94
|
+
|
|
83
95
|
function SelectFieldAnchor({ triggerRef, enabled }: SelectFieldAnchorProps) {
|
|
84
|
-
const { setTriggerPosition } =
|
|
96
|
+
const { triggerPosition, setTriggerPosition } =
|
|
97
|
+
SelectPrimitive.useRootContext()
|
|
98
|
+
const anchoredRef = React.useRef<TriggerPosition | null>(null)
|
|
99
|
+
|
|
100
|
+
React.useEffect(() => {
|
|
101
|
+
anchoredRef.current = triggerPosition ?? null
|
|
102
|
+
}, [triggerPosition])
|
|
85
103
|
|
|
86
104
|
React.useEffect(() => {
|
|
87
105
|
if (!enabled) return
|
|
88
106
|
|
|
89
|
-
|
|
107
|
+
let frame: number
|
|
108
|
+
let previous: TriggerPosition | null = null
|
|
109
|
+
|
|
110
|
+
const track = () => {
|
|
90
111
|
triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
|
|
91
|
-
|
|
112
|
+
const measured = { width, height, pageX, pageY }
|
|
113
|
+
const settled = isSamePosition(measured, previous)
|
|
114
|
+
if (settled && !isSamePosition(measured, anchoredRef.current)) {
|
|
115
|
+
anchoredRef.current = measured
|
|
116
|
+
setTriggerPosition(measured)
|
|
117
|
+
}
|
|
118
|
+
previous = measured
|
|
92
119
|
})
|
|
120
|
+
frame = requestAnimationFrame(track)
|
|
93
121
|
}
|
|
94
122
|
|
|
95
|
-
|
|
96
|
-
const hidden = Keyboard.addListener("keyboardDidHide", reanchor)
|
|
123
|
+
frame = requestAnimationFrame(track)
|
|
97
124
|
|
|
98
|
-
return () =>
|
|
99
|
-
shown.remove()
|
|
100
|
-
hidden.remove()
|
|
101
|
-
}
|
|
125
|
+
return () => cancelAnimationFrame(frame)
|
|
102
126
|
}, [enabled, setTriggerPosition, triggerRef])
|
|
103
127
|
|
|
104
128
|
return null
|