Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PoC --- feat(transfer): use native single-/multi selects #1556

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 53 additions & 33 deletions components/transfer/src/options-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,27 @@
import { spacers } from '@dhis2/ui-constants'
import PropTypes from 'prop-types'
import React, { Fragment, useRef } from 'react'
import { EndIntersectionDetector } from './end-intersection-detector.js'

Check failure on line 5 in components/transfer/src/options-container.js

View workflow job for this annotation

GitHub Actions / lint

'EndIntersectionDetector' is defined but never used
import { useResizeCounter } from './use-resize-counter.js'

export const OptionsContainer = ({
dataTest,
emptyComponent,
onEndReached,

Check failure on line 11 in components/transfer/src/options-container.js

View workflow job for this annotation

GitHub Actions / lint

'onEndReached' is defined but never used
getOptionClickHandlers,
highlightedOptions,
loading,
maxSelections,
renderOption,
options,
selected,
selectionHandler,
setHighlightedOptions,
toggleHighlightedOption,
}) => {
const optionsRef = useRef(null)
const wrapperRef = useRef(null)
const resizeCounter = useResizeCounter(wrapperRef.current)

Check failure on line 25 in components/transfer/src/options-container.js

View workflow job for this annotation

GitHub Actions / lint

'resizeCounter' is assigned a value but never used

return (
<div className="optionsContainer">
Expand All @@ -31,51 +33,62 @@
)}

<div className="container" data-test={dataTest} ref={optionsRef}>
<div className="content-container" ref={wrapperRef}>
{!options.length && emptyComponent}
{options.map((option) => {
const highlighted = !!highlightedOptions.find(
(highlightedSourceOption) =>
highlightedSourceOption === option.value
)
{!options.length && emptyComponent}
{!!options.length && (
<select
multiple={maxSelections === Infinity}
size={maxSelections === 1 ? 2 : undefined}
className="content-container"
ref={wrapperRef}
onChange={(e) => {
const nextSelected = [...e.target.options].reduce(
(curNextSelected, option) => {
if (!option.selected) {
return curNextSelected
}

return (
<Fragment key={option.value}>
{renderOption({
...option,
...getOptionClickHandlers(
option,
selectionHandler,
toggleHighlightedOption
),
highlighted,
selected,
})}
</Fragment>
)
})}
return [...curNextSelected, option.value]
},
[]
)
setHighlightedOptions(nextSelected)
}}
>
{options.map((option, index) => {
const isLast = index === options.length - 1

Check failure on line 58 in components/transfer/src/options-container.js

View workflow job for this annotation

GitHub Actions / lint

'isLast' is assigned a value but never used
const highlighted = !!highlightedOptions.find(
(highlightedSourceOption) =>
highlightedSourceOption === option.value
)

{onEndReached && (
<EndIntersectionDetector
dataTest={`${dataTest}-endintersectiondetector`}
key={`key-${resizeCounter}`}
rootRef={optionsRef}
onEndReached={onEndReached}
/>
)}
</div>
return (
<Fragment key={option.value}>
{renderOption({
...option,
...getOptionClickHandlers(
option,
selectionHandler,
toggleHighlightedOption
),
highlighted,
selected,
})}
</Fragment>
)
})}
</select>
)}
</div>

<style jsx>{`
.optionsContainer {
flex-grow: 1;
padding: ${spacers.dp4} 0;
position: relative;
overflow: hidden;
}

.container {
overflow-y: auto;
overflow: hidden;
height: 100%;
}

Expand All @@ -94,6 +107,11 @@
.content-container {
z-index: 1;
position: relative;
height: 100%;
width: 100%;
overflow: auto;
border: 0;
padding: ${spacers.dp4} 0;
}

.loading + .container .content-container {
Expand All @@ -111,6 +129,8 @@
OptionsContainer.propTypes = {
dataTest: PropTypes.string.isRequired,
getOptionClickHandlers: PropTypes.func.isRequired,
maxSelections: PropTypes.oneOf([1, Infinity]).isRequired,
setHighlightedOptions: PropTypes.func.isRequired,
emptyComponent: PropTypes.node,
highlightedOptions: PropTypes.arrayOf(PropTypes.string),
loading: PropTypes.bool,
Expand Down
53 changes: 16 additions & 37 deletions components/transfer/src/transfer-option.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,53 @@
import { colors } from '@dhis2/ui-constants'
import cx from 'classnames'
import PropTypes from 'prop-types'
import React, { useRef } from 'react'

const DOUBLE_CLICK_MAX_DELAY = 500
import React from 'react'

export const TransferOption = ({
className,
disabled,
dataTest,
highlighted,
onClick,

Check failure on line 10 in components/transfer/src/transfer-option.js

View workflow job for this annotation

GitHub Actions / lint

'onClick' is defined but never used
onDoubleClick,
label,
value,
}) => {
const doubleClickTimeout = useRef(null)

return (
<div
data-test={dataTest}
onClick={(event) => {
if (disabled) {
return
}

if (doubleClickTimeout.current) {
clearTimeout(doubleClickTimeout.current)
doubleClickTimeout.current = null

onDoubleClick({ value }, event)
} else {
doubleClickTimeout.current = setTimeout(() => {
clearTimeout(doubleClickTimeout.current)
doubleClickTimeout.current = null
}, DOUBLE_CLICK_MAX_DELAY)

onClick({ value }, event)
}
}}
data-value={value}
className={cx(className, { highlighted, disabled })}
>
{label}
<>
<option
className={cx(className, { disabled })}
data-test={dataTest}
data-value={value}
value={value}
onDoubleClick={(e) => onDoubleClick({ value }, event)}

Check failure on line 22 in components/transfer/src/transfer-option.js

View workflow job for this annotation

GitHub Actions / lint

'e' is defined but never used
>
{label}
</option>

<style jsx>{`
div {
option {
font-size: 14px;
line-height: 16px;
padding: 4px 8px;
color: ${colors.grey900};
user-select: none;
}

div:hover {
option:hover {
background: ${colors.grey200};
}

div.highlighted {
option:checked {
background: ${colors.teal700};
color: ${colors.white};
}

div.disabled {
option.disabled {
color: ${colors.grey600};
cursor: not-allowed;
}
`}</style>
</div>
</>
)
}

Expand All @@ -81,7 +61,6 @@
className: PropTypes.string,
dataTest: PropTypes.string,
disabled: PropTypes.bool,
highlighted: PropTypes.bool,
onClick: PropTypes.func,
onDoubleClick: PropTypes.func,
}
4 changes: 4 additions & 0 deletions components/transfer/src/transfer.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ export const Transfer = ({
)}

<OptionsContainer
maxSelections={maxSelections}
setHighlightedOptions={setHighlightedSourceOptions}
dataTest={`${dataTest}-sourceoptions`}
emptyComponent={sourceEmptyPlaceholder}
getOptionClickHandlers={getOptionClickHandlers}
Expand Down Expand Up @@ -324,6 +326,8 @@ export const Transfer = ({
)}

<OptionsContainer
maxSelections={maxSelections}
setHighlightedOptions={setHighlightedPickedOptions}
selected
dataTest={`${dataTest}-pickedoptions`}
emptyComponent={selectedEmptyComponent}
Expand Down
Loading