Skip to content
This repository was archived by the owner on Aug 2, 2024. It is now read-only.

Commit bd4e533

Browse files
fixes combobox bug
2 clicks were needed to show the combobox
1 parent 8280a50 commit bd4e533

3 files changed

Lines changed: 119 additions & 43 deletions

File tree

src/components/Combobox/Combobox.tsx

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ export class Combobox extends mixins(UidMixin) {
4343

4444
public togglePopoverVisible() {
4545
this.currentPopoverVisible = !this.currentPopoverVisible;
46-
this.$emit('update:currentPopoverVisible', this.currentPopoverVisible);
4746
}
4847

4948
private setCurrentValue(newValue: string | number | null) {
@@ -73,29 +72,38 @@ export class Combobox extends mixins(UidMixin) {
7372
on-click={this.handleMenuItemClick}
7473
noArrow={true}
7574
popoverVisible={this.currentPopoverVisible}
75+
on-visible={(visible: boolean) => this.currentPopoverVisible = visible }
76+
{...
77+
{
78+
scopedSlots: {
79+
control: (scope: { toggle: () => (void) }) => {
80+
return (<div staticClass='fd-combobox-control'>
81+
<InputGroup
82+
compact={this.compact}
83+
afterClass='fd-input-group__addon--button'
84+
>
85+
<Input
86+
id={this.uid}
87+
value={this.value}
88+
compact={this.compact}
89+
nativeOn-click={scope.toggle}
90+
nativeOn-keyup={this.handleKeyup}
91+
on-input={this.setCurrentValue}
92+
placeholder={this.placeholder}
93+
/>
94+
<Button
95+
slot='after'
96+
on-click={scope.toggle}
97+
icon='navigation-down-arrow'
98+
styling='light'
99+
/>
100+
</InputGroup></div>
101+
);
102+
},
103+
},
104+
}
105+
}
76106
>
77-
<div class='fd-combobox-control' slot='control'>
78-
<InputGroup
79-
compact={this.compact}
80-
afterClass='fd-input-group__addon--button'
81-
>
82-
<Input
83-
id={this.uid}
84-
value={this.value}
85-
compact={this.compact}
86-
nativeOn-click={() => this.currentPopoverVisible = true}
87-
nativeOn-keyup={this.handleKeyup}
88-
on-input={this.setCurrentValue}
89-
placeholder={this.placeholder}
90-
/>
91-
<Button
92-
slot='after'
93-
on-click={this.togglePopoverVisible}
94-
icon='navigation-down-arrow'
95-
styling='light'
96-
/>
97-
</InputGroup>
98-
</div>
99107
{dropdown}
100108
</Popover>
101109
</div>

src/components/Popover/Popover.tsx

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,55 @@ export class Popover extends mixins(UidMixin) {
6262

6363
private popoverTriggerControl: Element | null = null;
6464

65+
// There are three different ways a trigger control can be rendered.
66+
// 1. $slots.control:
67+
// If there is a slot called 'control' we simply render that and assume that
68+
// the control rendered emits 'click'-events.
69+
// 2. $scopedSlots.control:
70+
// If there is a scoped slot 'control' we assume that the popover consumer
71+
// has some kind of special needs. Thus, when rendering the scoped slot,
72+
// we pass a little bit of context to the consumer. Shape of the scope/context
73+
// passed to the consumer:
74+
// {
75+
// // Calling this will toggle the popover.
76+
// toggle: () => (void);
77+
// // whether the popover is currently visible.
78+
// visible: bool;
79+
// }
80+
// Because of the fact that we assume that the consumer has special needs,
81+
// we do not show the popover automatically. The consumer has to call `toggle`
82+
// for example by binding it to @click/@click.native of some kind of control
83+
// or by using v-model.
84+
// 3. If there is no (scoped) control-slot we simply render a standard button
85+
// on behalf of the consumer.
86+
private renderTriggerControl() {
87+
const control = this.$slots.control;
88+
if(control != null) {
89+
return <div on-click={this.toggle} role='button'>{control}</div>;
90+
}
91+
const renderControl = this.$scopedSlots.control;
92+
if(renderControl != null) {
93+
const context = {
94+
toggle: this.toggle,
95+
visible: this.currentPopoverVisible,
96+
};
97+
return renderControl(context);
98+
}
99+
return (
100+
<Button
101+
staticClass='fd-popover__control'
102+
aria-controls={this.uid}
103+
aria-expanded={this.currentPopoverVisible}
104+
aria-haspopup='true'
105+
on-click={this.toggle}
106+
>
107+
{this.title}
108+
</Button>
109+
);
110+
}
111+
65112
public render() {
66113
const dropdown = this.$slots.default || [];
67-
const triggerControl = this.$slots.control;
68114
const ignoredElementsHandler = () => {
69115
const el = this.popoverTriggerControl;
70116
if(el == null) { return []; }
@@ -74,18 +120,7 @@ export class Popover extends mixins(UidMixin) {
74120
return (
75121
<div class='fd-popover'>
76122
<div class='fd-popover__control' ref={(el: Element) => this.popoverTriggerControl = el}>
77-
{triggerControl && <div role='button' on-click={this.toggle}>{triggerControl}</div>}
78-
{!triggerControl &&
79-
<Button
80-
class='fd-popover__control'
81-
aria-controls={this.uid}
82-
aria-expanded={this.currentPopoverVisible}
83-
aria-haspopup='true'
84-
on-click={this.toggle}
85-
>
86-
{this.title}
87-
</Button>
88-
}
123+
{this.renderTriggerControl()}
89124
</div>
90125

91126
<ClickAwayContainer
Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,47 @@
11
<title>Popover with custom Trigger</title>
2+
<tip>If you have special needs simply use `slot-scope`. This gives you access to the `toggle`-function and the current `visibility`. The second button is using this in order to customize the trigger event and styling.</tip>
3+
<docs>
4+
**Implementation Details**
5+
6+
There are three different ways a trigger control can be rendered.
7+
8+
1. `$slots.control`: If there is a slot called `control` we simply render that. This control should emit `click`-events.
9+
2. `$scopedSlots.control`: If there is a scoped slot `control` we assume that the popover consumer (**you**) has some kind of special needs. Thus, when rendering the scoped slot, we pass a little bit of context to the consumer.
10+
11+
**The context looks like this:**:
12+
13+
```javascript
14+
{
15+
// Calling this will toggle the popover.
16+
toggle: () => (void);
17+
// whether the popover is currently visible.
18+
visible: boolean;
19+
}
20+
```
21+
22+
Because of the fact that we assume that the consumer has special needs, we do not show the popover automatically. The consumer has to call `toggle` for example by binding it to `@click`/`@click.native` of some kind of control or by using `v-model`.
23+
24+
3. If there is no (scoped) control-slot we simply render a standard button on behalf of the consumer.
25+
</docs>
26+
<template><div>
27+
<FdPopover>
28+
<FdButton styling="emphasized" type="positive" slot="control">Custom Popover Trigger Control (automatic popover visibility)</FdButton>
29+
<FdMenuItem>Option 1</FdMenuItem>
30+
<FdMenuItem>Option 2</FdMenuItem>
31+
<FdMenuItem>Option 3</FdMenuItem>
32+
</FdPopover>
33+
34+
<br /><br />
235

3-
<template>
4-
<!-- div is needed: otherwise the popover will not be positioned correctly -->
5-
<div>
636
<FdPopover>
7-
<FdButton styling="emphasized" type="positive" slot="control">Custom Popover Trigger Control</FdButton>
37+
<FdButton
38+
slot-scope="popover"
39+
slot="control"
40+
:type="popover.visible ? 'negative' : 'warning'"
41+
@click="popover.toggle"
42+
>Custom Popover Trigger Control (manual popover visibility)</FdButton>
843
<FdMenuItem>Option 1</FdMenuItem>
944
<FdMenuItem>Option 2</FdMenuItem>
1045
<FdMenuItem>Option 3</FdMenuItem>
11-
<FdMenuItem>Option 4</FdMenuItem>
1246
</FdPopover>
13-
</div>
14-
</template>
47+
</div></template>

0 commit comments

Comments
 (0)