Skip to content

Commit

Permalink
feat(Autocomplete): add support for data suffix_value
Browse files Browse the repository at this point in the history
  • Loading branch information
tujoworker committed Jun 16, 2022
1 parent d1b1b19 commit 73bcf7d
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const data = [

// (optional) is show instead of "content", once selected
selected_value: 'Item 1 Value',
suffix_value: 'Addition 1',

// Item content as a string, array or React Element
content: 'Item 1 Content',
Expand All @@ -90,6 +91,7 @@ const data = [
{
selected_key: 'key_2',
selected_value: 'Item 3 Value',
suffix_value: 'Addition 3',
content: (
<Autocomplete.HorizontalItem>
<IconPrimary icon="bell" />
Expand All @@ -100,6 +102,7 @@ const data = [
{
selected_key: 'key_3',
selected_value: 'Item 4 Value',
suffix_value: 'Addition 4',
content: ['Item 4 Content A', <>Custom Component</>],
},
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ You may check out the [DrawerList Properties](#drawerlist-properties) down below

| Properties | Description |
| ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title` | _(optional)_ give a title to let the users know what they have to do. Defaults to `Valgmeny`. |
| `variant` | _(optional)_ defines the kind of dropdown. Possible values are `primary`, `secondary`, `tertiary` and `signal`. Defaults to `secondary`. |
| `title` | _(optional)_ give a title to let the users know what they have to do. Defaults to `Valgmeny`. |
| `variant` | _(optional)_ defines the kind of dropdown. Possible values are `primary`, `secondary`, `tertiary` and `signal`. Defaults to `secondary`. |
| `icon` | _(optional)_ icon to be included in the dropdown. |
| `icon_size` | _(optional)_ change the size of the icon pragmatically. |
| `icon_position` | _(optional)_ position of the icon inside the dropdown. Set to `left` or `right`. Defaults to `right`. |
| `icon_position` | _(optional)_ position of the icon inside the dropdown. Set to `left` or `right`. Defaults to `right`. |
| `triangle_position` | _(optional)_ position of arrow / triangle of the drawer. Set to `left` or `right`. Defaults to `right`. |
| `size` | _(optional)_ define the height of the Dropdown. Can be set to `small`, `default`, `medium` and `large`. Defaults to `default`. |
| `opened` | _(optional)_ if set to `true`, the Dropdown will be rendered initially with a visible and accessible data list / options. |
| `open_on_focus` | _(optional)_ if set to `true`, the Dropdown will be opened when the users enter the trigger button with a focus action. |
| `open_on_focus` | _(optional)_ if set to `true`, the Dropdown will be opened when the users enter the trigger button with a focus action. |
| `prevent_selection` | _(optional)_ if set to `true`, no permanent selection will be made. Defaults to `false`. |
| `action_menu` | _(optional)_ same as `prevent_selection`, but the DrawerList will be opened from the bottom of the page for mobile devices. Defaults to `false`. |
| `more_menu` | _(optional)_ same as `prevent_selection`, but the "selection area" (given title) will not be visible and the icon `more` (three dots) is used. Defaults to `false`. |
Expand All @@ -27,7 +27,7 @@ You may check out the [DrawerList Properties](#drawerlist-properties) down below
| `skip_portal` | _(optional)_ set to `true` to disable the React Portal behavior. Defaults to `false`. |
| `stretch` | _(optional)_ if set to `true`, then the dropdown will be 100% in available `width`. |
| `status` | _(optional)_ text with a status message. The style defaults to an error message. You can use `true` to only get the status color, without a message. |
| `status_state` | _(optional)_ defines the state of the status. Currently, there are two statuses `[error, info]`. Defaults to `error`. |
| `status_state` | _(optional)_ defines the state of the status. Currently, there are two statuses `[error, info]`. Defaults to `error`. |
| `status_props` | _(optional)_ use an object to define additional FormStatus properties. |
| `global_status_id` | _(optional)_ the `status_id` used for the target [GlobalStatus](/uilib/components/global-status). |
| `label` | _(optional)_ prepends the Form Label component. If no ID is provided, a random ID is created. |
Expand All @@ -53,6 +53,7 @@ const data = [

// (optional) is show instead of "content", once selected
selected_value: 'Item 1 Value',
suffix_value: 'Addition 1',

// Item content as a string or array
content: 'Item 1 Content',
Expand All @@ -66,11 +67,13 @@ const data = [
{
selected_key: 'key_2',
selected_value: 'Item 3 Value',
suffix_value: 'Addition 3',
content: ['Item 3 Content A', 'Item 3 Content B'],
},
{
selected_key: 'key_3',
selected_value: 'Item 4 Value',
suffix_value: 'Addition 4',
content: ['Item 4 Content A', <>Custom Component</>],
},
]
Expand All @@ -80,4 +83,4 @@ const data = {
a: 'A',
b: 'B',
}
```
```
17 changes: 16 additions & 1 deletion packages/dnb-eufemia/src/components/autocomplete/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ export default class Autocomplete extends React.PureComponent {
PropTypes.string,
PropTypes.node,
]),
suffix_value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node,
]),
content: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node,
Expand Down Expand Up @@ -388,6 +392,11 @@ class AutocompleteInstance extends React.PureComponent {
})
}

static getCurrentDataSuffix(selected_item, data) {
const currentData = getCurrentData(selected_item, data)
return currentData?.suffix_value
}

static getDerivedStateFromProps(props, state) {
if (state._listenForPropChanges) {
state.disableHighlighting = isTrue(props.disable_highlighting)
Expand Down Expand Up @@ -1744,7 +1753,7 @@ class AutocompleteInstance extends React.PureComponent {
}

const inputParams = {
className: classnames('dnb-autocomplete__input'),
className: 'dnb-autocomplete__input',
id,
value: inputValue,
autoCapitalize: 'none',
Expand Down Expand Up @@ -1835,6 +1844,11 @@ class AutocompleteInstance extends React.PureComponent {
)
}

const innerSuffix = AutocompleteInstance.getCurrentDataSuffix(
this.context.drawerList.selected_item,
this.context.drawerList.original_data
)

// also used for code markup simulation
validateDOMAttributes(null, mainParams)
validateDOMAttributes(null, shellParams)
Expand Down Expand Up @@ -1900,6 +1914,7 @@ class AutocompleteInstance extends React.PureComponent {
ref={this._refInput}
{...inputParams}
{...status_props}
inner_element={innerSuffix}
/>
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1330,6 +1330,89 @@ describe('Autocomplete component', () => {
expect(callThree.dataList).toStrictEqual(callTwo.dataList)
})

it('will use selected_value as the input value when selected', () => {
const mockData = [
{ selected_value: 'a value', content: '11 aa' },
{ selected_value: 'b value', content: '22 bb' },
{ selected_value: 'c value', content: '22 cc' },
]

let index = 1

const Comp = mount(
<Component {...mockProps} value={index} data={mockData} />
)

const assert = () => {
expect(Comp.find('.dnb-input__input').instance().value).toBe(
mockData[index].selected_value
)
}

assert()

index = 2
Comp.setProps({ value: index })

assert()
})

it('will show suffix_value when selected', () => {
const mockData = [
{
selected_value: 'a selected',
suffix_value: 'a suffix',
content: '11 aa',
},
{
selected_value: 'b selected',
suffix_value: 'b suffix',
content: '22 bb',
},
{
selected_value: 'c selected',
suffix_value: 'c suffix',
content: '22 cc',
},
]

let index = 1

const Comp = mount(
<Component {...mockProps} value={index} data={mockData} />
)

const assertInputValue = () => {
expect(Comp.find('.dnb-input__input').instance().value).toBe(
mockData[index].selected_value
)
}

const assertSuffixItem = (itemIndex) => {
expect(
Comp.find('.dnb-drawer-list__option')
.at(itemIndex)
.instance()
.querySelector(
'.dnb-drawer-list__option__item.dnb-drawer-list__option__suffix'
).textContent
).toBe(mockData[itemIndex].suffix_value)
}

assertInputValue()

index = 2
Comp.setProps({ value: index })

assertInputValue()

// open
keydown(Comp, 40) // down

assertSuffixItem(1)
assertSuffixItem(2)
})

it('will select correct item after updateData', () => {
const mockData = [
{ selected_value: 'a value', content: '11 aa' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2869,6 +2869,19 @@ legend.dnb-form-label {
transition: transform 400ms ease-out; }
.dnb-autocomplete__shell, .dnb-autocomplete__input {
border-radius: var(--autocomplete-border-radius); }
.dnb-autocomplete .dnb-input__inner__element {
position: absolute;
right: 0; }
.dnb-autocomplete .dnb-input__inner__element.dnb-p {
pointer-events: none;
white-space: nowrap; }
@media screen and (max-width: 50em) {
.dnb-autocomplete .dnb-input__inner__element.dnb-p {
display: none; } }
.dnb-autocomplete--icon-position-right .dnb-input__inner__element {
padding-right: 3rem; }
.dnb-autocomplete--icon-position-right .dnb-input--icon-position-right .dnb-autocomplete--icon-position-right .dnb-input--icon-position-right.dnb-autocomplete--icon-position-right .dnb-input--has-icon .dnb-autocomplete--icon-position-right .dnb-input__input {
padding-right: 1rem; }
.dnb-autocomplete__text {
order: 1;
position: relative;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -832,3 +832,62 @@ function UpdateDataExample() {
</>
)
}

const WideStyle = styled.div`
.dnb-autocomplete__shell {
/* custom width */
width: 50vw;
min-width: 15rem;
max-width: 30rem;
}
`

function rand() {
return Math.round(Math.random() * 1000000)
}

export function AdditionalValue() {
const numbers = [
{
selected_value: `Brukskonto (${format(rand(), { ban: true })})`,
suffix_value: format(rand(), { currency: true }),
content: ['Brukskonto', format(rand(), { ban: true })],
},
{
selected_value: `BSU (${format(rand(), { ban: true })})`,
suffix_value: format(rand(), { currency: true }),
content: ['BSU', format(rand(), { ban: true })],
},
{
selected_value: `Sparekonto (${format(rand(), { ban: true })})`,
suffix_value: format(rand(), { currency: true }),
content: ['Sparekonto', format(rand(), { ban: true })],
},
{
selected_value: `Brukskonto (${format(rand(), { ban: true })})`,
suffix_value: format(rand(), { currency: true }),
content: ['Brukskonto', format(rand(), { ban: true })],
},
]

return (
<WideStyle>
<Autocomplete
width="200px"
value={0}
// opened
// no_animation
size="medium"
// suffix="suffix"
// input_value="201"
input_icon="chevron_down"
icon_position="right"
label="From account"
label_direction="vertical"
data={numbers}
// show_clear_button
// search_numbers={true}
/>
</WideStyle>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,30 @@
border-radius: var(--autocomplete-border-radius);
}

// Support for "suffix_value"
.dnb-input {
&__inner__element {
position: absolute;
right: 0;
}
&__inner__element.dnb-p {
pointer-events: none;
white-space: nowrap;

@include allBelow(medium) {
display: none;
}
}
}
&--icon-position-right .dnb-input {
&__inner__element {
padding-right: 3rem;
}
&--icon-position-right &--icon-position-right#{&}--has-icon &__input {
padding-right: 1rem;
}
}

&__text {
order: 1;
position: relative;
Expand Down
19 changes: 15 additions & 4 deletions packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,6 @@ class DrawerListInstance extends React.PureComponent {
children && (
<span className="dnb-drawer-list__content">
{children}
{/* <Triangle /> */}
<span
className="dnb-drawer-list__triangle"
ref={_refTriangle}
Expand Down Expand Up @@ -558,19 +557,31 @@ DrawerList.Item.defaultProps = {
}

const ItemContent = ({ hash, children }) => {
let content = children

if (Array.isArray(children.content || children)) {
return (children.content || children).map((item, n) => (
content = (children.content || children).map((item, n) => (
<span key={hash + n} className="dnb-drawer-list__option__item">
{children.render ? children.render(item, hash + n) : item}
</span>
))
} else if (Object.prototype.hasOwnProperty.call(children, 'content')) {
return children.render
content = children.render
? children.render(children.content, hash, children)
: children.content
}

return children
return Object.prototype.hasOwnProperty.call(children, 'suffix_value') ? (
<>
{content}

<span className="dnb-drawer-list__option__item dnb-drawer-list__option__suffix">
{children.suffix_value}
</span>
</>
) : (
content
)
}
ItemContent.propTypes = {
hash: PropTypes.string.isRequired,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export const drawerListPropTypes = {
PropTypes.string,
PropTypes.node,
]),
suffix_value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node,
]),
content: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node,
Expand Down
Loading

0 comments on commit 73bcf7d

Please sign in to comment.