Skip to content

Commit

Permalink
feat: make infinite loading work for simple transfer
Browse files Browse the repository at this point in the history
  • Loading branch information
flaminic committed Oct 31, 2024
1 parent 2a04671 commit 8970f50
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 81 deletions.
80 changes: 74 additions & 6 deletions components/simple-transfer/src/options-container.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { spacers } from '@dhis2/ui-constants'
import { CircularLoader } from '@dhis2-ui/loader'
import PropTypes from 'prop-types'
import React, { Fragment, useRef } from 'react'
import React, { Fragment, useEffect, useRef } from 'react'
import { SimpleTransferOption } from './simple-transfer-option.js'

export const OptionsContainer = ({
dataTest,
emptyComponent,
onEndReached,
highlightedOptions,
loading,
maxSelections,
Expand All @@ -14,8 +16,37 @@ export const OptionsContainer = ({
selectionHandler,
setHighlightedOptions,
}) => {
const optionsRef = useRef(null)
const selectRef = useRef(null)
const wrapperRef = useRef(null)
// const resizeCounter = useResizeCounter(wrapperRef.current)
const lastOptionRef = useRef(null)

useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
onEndReached && onEndReached()
}
})
},
{
root: wrapperRef.current,
threshold: 1.0,
}
)

if (lastOptionRef.current) {
observer.observe(lastOptionRef.current)
}

return () => {
if (lastOptionRef.current) {
observer.unobserve(lastOptionRef.current)
}
observer.disconnect()
}
}, [options])

return (
<div className="optionsContainer">
Expand All @@ -25,14 +56,14 @@ export const OptionsContainer = ({
</div>
)}

<div className="container" data-test={dataTest} ref={optionsRef}>
<div className="container" data-test={dataTest} ref={wrapperRef}>
{!options.length && emptyComponent}
{!!options.length && (
<select
ref={selectRef}
className="content-select"
multiple={maxSelections === Infinity}
size={maxSelections === 1 ? 2 : undefined}
className="content-container"
ref={wrapperRef}
onChange={(e) => {
const nextSelected = [...e.target.options].reduce(
(curNextSelected, option) => {
Expand All @@ -47,7 +78,7 @@ export const OptionsContainer = ({
setHighlightedOptions(nextSelected)
}}
>
{options.map((option) => {
{options.map((option, index) => {
const highlighted = !!highlightedOptions.find(
(highlightedSourceOption) =>
highlightedSourceOption === option.value
Expand All @@ -61,6 +92,11 @@ export const OptionsContainer = ({
highlighted={highlighted}
selected={selected}
onDoubleClick={selectionHandler}
lastOptionReference={
index === options.length - 1
? lastOptionRef
: undefined
}
/>
</Fragment>
)
Expand All @@ -70,6 +106,37 @@ export const OptionsContainer = ({
</div>

<style jsx>{`
.optionsContainer {
flex-grow: 1;
padding: ${spacers.dp4} 0;
position: relative;
overflow: hidden;
}
.container {
overflow-y: auto;
height: 100%;
}
.loading {
display: flex;
height: 100%;
width: 100%;
align-items: center;
justify-content: center;
position: absolute;
z-index: 2;
top: 0;
inset-inline-start: 0;
}
.content-select {
border: none;
position: relative;
height: 100%;
width: 100%;
}
.loading + .container .content-container {
filter: blur(2px);
}
Expand All @@ -93,4 +160,5 @@ OptionsContainer.propTypes = {
),
selected: PropTypes.bool,
selectionHandler: PropTypes.func,
onEndReached: PropTypes.func,
}
3 changes: 3 additions & 0 deletions components/simple-transfer/src/simple-transfer-option.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const SimpleTransferOption = ({
onDoubleClick,
label,
value,
lastOptionReference,
}) => {
return (
<>
Expand All @@ -19,6 +20,7 @@ export const SimpleTransferOption = ({
data-value={value}
value={value}
onDoubleClick={() => onDoubleClick({ value }, event)}
ref={lastOptionReference}
>
{label}
</option>
Expand Down Expand Up @@ -56,5 +58,6 @@ SimpleTransferOption.propTypes = {
className: PropTypes.string,
dataTest: PropTypes.string,
disabled: PropTypes.bool,
lastOptionReference: PropTypes.object,
onDoubleClick: PropTypes.func,
}
76 changes: 1 addition & 75 deletions components/simple-transfer/src/simple-transfer.prod.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { SingleSelectField, SingleSelectOption } from '@dhis2-ui/select'
import { Tab, TabBar } from '@dhis2-ui/tab'
import PropTypes from 'prop-types'
import React, { useEffect, useState } from 'react'
import { SimpleTransferOption } from './simple-transfer-option.js'
import { SimpleTransfer } from './simple-transfer.js'

const subtitle = 'Allows users to select options from a list'
Expand Down Expand Up @@ -218,79 +217,6 @@ FilteredPlaceholder.args = {
filterPlaceholder: 'Search',
}

const renderOption = ({ label, value, onClick, highlighted, selected }) => (
<p
onClick={(event) => onClick({ label, value }, event)}
style={{
background: highlighted ? 'green' : 'blue',
color: selected ? 'orange' : 'white',
}}
>
Custom: {label} (label), {value} (value)
</p>
)

const RenderOptionCode = () => (
<>
<strong>Custom option code:</strong>
<code>
<pre>{`const renderOption = ({ label, value, onClick, highlighted, selected }) => (
<p
onClick={event => onClick({ label, value }, event)}
style={{
background: highlighted ? 'green' : 'blue',
color: selected ? 'orange' : 'white',
}}
>
Custom: {label} (label), {value} (value)
</p>
)`}</pre>
</code>
</>
)

const StatefulTemplateCustomRenderOption = ({
initiallySelected = [],
...args
}) => {
const [selected, setSelected] = useState(initiallySelected)
const onChange = (payload) => setSelected(payload.selected)

return <SimpleTransfer {...args} selected={selected} onChange={onChange} />
}
StatefulTemplateCustomRenderOption.propTypes = {
initiallySelected: PropTypes.array,
}

export const CustomListOptions = (args) => (
<>
<RenderOptionCode />
<StatefulTemplateCustomRenderOption {...args} />
</>
)
CustomListOptions.args = {
renderOption,
options: options.slice(0, 2),
initiallySelected: options.slice(0, 2).map(({ value }) => value),
}

export const IndividualCustomOption = StatefulTemplateCustomRenderOption.bind(
{}
)
IndividualCustomOption.args = {
addAllText: 'Add all',
addIndividualText: 'Add individual',
removeAllText: 'Remove all',
removeIndividualText: 'Remove individual',
renderOption: (option) => {
if (option.value === options[0].value) {
return renderOption(option)
}

return <SimpleTransferOption {...option} />
},
}

export const CustomButtonText = StatefulTemplate.bind({})
CustomButtonText.args = {
addAllText: 'Add all',
Expand Down Expand Up @@ -482,7 +408,7 @@ export const InfiniteLoading = (args) => {
// state for whether the next page's options are being loaded
const [loading, setLoading] = useState(false)
// captures the current page
const [page, setPage] = useState(0)
const [page, setPage] = useState(1)
// all options (incl. available AND selected options)
const [options, setOptions] = useState([])
// selected options
Expand Down

0 comments on commit 8970f50

Please sign in to comment.