fix(admin-ui): Make regions in tax setting scrollable (#5530)
* fix overflow * intersection observer and changeset
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/admin-ui": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(admin-ui): Fixes an issue where regions were not scrollable in tax settings view, also adds a IntersectionObserver to ensure that we load all regions as the user scrolls. The current implementation was capped at loading 20 regions.
|
||||||
@@ -1,23 +1,76 @@
|
|||||||
|
import { Region } from "@medusajs/medusa"
|
||||||
import { useAdminRegions } from "medusa-react"
|
import { useAdminRegions } from "medusa-react"
|
||||||
import { useCallback, useEffect, useState } from "react"
|
import { useCallback, useEffect, useRef, useState } from "react"
|
||||||
import { useNavigate, useParams } from "react-router-dom"
|
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { useNavigate, useParams } from "react-router-dom"
|
||||||
import BackButton from "../../../components/atoms/back-button"
|
import BackButton from "../../../components/atoms/back-button"
|
||||||
import Spinner from "../../../components/atoms/spinner"
|
|
||||||
import GearIcon from "../../../components/fundamentals/icons/gear-icon"
|
import GearIcon from "../../../components/fundamentals/icons/gear-icon"
|
||||||
import BodyCard from "../../../components/organisms/body-card"
|
import BodyCard from "../../../components/organisms/body-card"
|
||||||
import RadioGroup from "../../../components/organisms/radio-group"
|
import RadioGroup from "../../../components/organisms/radio-group"
|
||||||
import TwoSplitPane from "../../../components/templates/two-split-pane"
|
import TwoSplitPane from "../../../components/templates/two-split-pane"
|
||||||
import TaxDetails from "./details"
|
import TaxDetails from "./details"
|
||||||
|
|
||||||
|
const LIMIT = 20
|
||||||
|
|
||||||
const Taxes = () => {
|
const Taxes = () => {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const regId: string | undefined = params["*"]
|
const regId: string | undefined = params["*"]
|
||||||
|
|
||||||
|
const [offset, setOffset] = useState(0)
|
||||||
|
const observerRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { regions, isLoading } = useAdminRegions()
|
const [regionOpts, setRegionOpts] = useState<Region[]>([])
|
||||||
|
|
||||||
|
const { regions, count, isLoading } = useAdminRegions({
|
||||||
|
offset,
|
||||||
|
limit: LIMIT,
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (regions && !isLoading) {
|
||||||
|
setRegionOpts((prev) => {
|
||||||
|
const exisitngIds = prev.map((r) => r.id)
|
||||||
|
const newOpts = regions.filter((r) => !exisitngIds.includes(r.id))
|
||||||
|
|
||||||
|
return [...prev, ...newOpts]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [regions, isLoading])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const current = observerRef.current
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
// If the ref is in view and there's more data to fetch
|
||||||
|
if (
|
||||||
|
entries[0].isIntersecting &&
|
||||||
|
regionOpts &&
|
||||||
|
regionOpts.length < (count || 0)
|
||||||
|
) {
|
||||||
|
setOffset(regionOpts.length)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
root: null, // relative to the viewport
|
||||||
|
threshold: 1.0, // trigger when 100% of the ref is visible
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (current) {
|
||||||
|
observer.observe(current)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the observer on component unmount
|
||||||
|
return () => {
|
||||||
|
if (current) {
|
||||||
|
observer.unobserve(current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [count, regionOpts])
|
||||||
|
|
||||||
const [selectedRegion, setSelectedRegion] = useState<string | undefined>(
|
const [selectedRegion, setSelectedRegion] = useState<string | undefined>(
|
||||||
regId
|
regId
|
||||||
@@ -71,35 +124,40 @@ const Taxes = () => {
|
|||||||
onClick: () => navigate("/a/settings/regions"),
|
onClick: () => navigate("/a/settings/regions"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
className="no-scrollbar overflow-y-auto"
|
||||||
>
|
>
|
||||||
{isLoading || !regions ? (
|
<div>
|
||||||
<div className="flex h-full flex-grow items-center justify-center">
|
|
||||||
<Spinner size="large" variant="secondary" />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<RadioGroup.Root
|
<RadioGroup.Root
|
||||||
value={selectedRegion}
|
value={selectedRegion}
|
||||||
onValueChange={handleChange}
|
onValueChange={handleChange}
|
||||||
>
|
>
|
||||||
{regions.map((r) => {
|
{regionOpts.map((r, index) => {
|
||||||
return (
|
return (
|
||||||
<RadioGroup.Item
|
<div
|
||||||
label={r.name}
|
key={r.id}
|
||||||
description={
|
ref={
|
||||||
r.countries.length
|
index === regionOpts.length - 1
|
||||||
? `${r.countries
|
? observerRef
|
||||||
.map((c) => c.display_name)
|
|
||||||
.join(", ")}`
|
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
value={r.id}
|
>
|
||||||
key={r.id}
|
<RadioGroup.Item
|
||||||
id={r.id}
|
label={r.name}
|
||||||
/>
|
description={
|
||||||
|
r.countries.length
|
||||||
|
? `${r.countries
|
||||||
|
.map((c) => c.display_name)
|
||||||
|
.join(", ")}`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
value={r.id}
|
||||||
|
key={r.id}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</RadioGroup.Root>
|
</RadioGroup.Root>
|
||||||
)}
|
</div>
|
||||||
</BodyCard>
|
</BodyCard>
|
||||||
<TaxDetails id={selectedRegion} />
|
<TaxDetails id={selectedRegion} />
|
||||||
</TwoSplitPane>
|
</TwoSplitPane>
|
||||||
|
|||||||
Reference in New Issue
Block a user