darcy-framework/darcy-ui

Add Evented interface (name not final)

Opened this issue · 3 comments

API like...

Event<Element> event(String eventName)

Each element type will have to override unfortunately so that Event is typed with the more specific element.

Questions:

  • Should event name (or key) be any different than String? Object? Generic type? (implements Evented<String> vs Evented<EventDescriptor> or whatever an implementation might need)
  • Should it always return Event<Element>?
  • Do some generic craziness like Evented<T extends Evented<T>> to return Event<T> ?
  • Don't accept string, accept a thing that determines event return type? <T> Event<T> event(EventDesc<T> event)

Great! I can tackle this one since it looks related to my other event API task (if I'm misunderstanding, stop me here). If not, I'll start with some questions then!

Going over our notes from the other day:

  • We discussed using something like:
public interface Evented {
    <T> Event<T> event(String eventName);
}
// ...
Event<Element> changeEvent = element.event("change");
Element element = changeEvent.waitUpTo(1, MINUTES);
  • So then in this, we want Evented (or whatever the name may be, Evented/HasEvents) to be extended by the Element interface in darcy-ui therefore all elements have the API? Or did we want to put this interface in darcy-ui then leave the implementations dealing with various DOM events to HtmlElement?
    • You mentioned: Each element type will have to override unfortunately so that Event is typed with the more specific element.
    • Could you give a short example of what this would look like with the above way of doing this? Once element.event("change"); is called via waitUpTo, is it as simple as running the change event on said element and returning the result (whatever that may be, in this case the original element).

There was also the possibility of:

element.on("change");

As the API. I guess this relates to the previous question, I was taking a look at this option and was wondering how this approach would look.

public interface Evented {
    <T> Event<T> on(String eventName);
}

Just something like that? I guess thinking about it that's more of a naming concern if it may do the same thing implementation wise.

What would the EventDescriptor look like? Some sort of wrapper for the common events?

Some of this discussion may go in the other issue I'm assigned so I can move that over there if it seems more relevant.

Its actually more relevant here since the high level event api will go in
darcy-ui. Web specifics can go in darcy-web but you have to start here

On Tue, Nov 11, 2014, 9:58 AM Derek McNeil notifications@github.com wrote:

Great! I can tackle this one since it looks related to my other event API
task (if I'm misunderstanding, stop me here). If not, I'll start with some
questions then!

Going over our notes from the other day:

  • We discussed using something like:

public interface Evented {
Event event(String eventName);
}// ...Event changeEvent = element.event("change");Element element = changeEvent.waitUpTo(1, MINUTES);

  • So then in this, we want Evented (or whatever the name may be,
    Evented/HasEvents) to be extended by the Element interface in darcy-ui
    therefore all elements have the API? Or did we want to put this interface
    in darcy-ui then leave the implementations dealing with various DOM events
    to HtmlElement?
    • You mentioned: Each element type will have to override
      unfortunately so that Event is typed with the more specific element.
    • Could you give a short example of what this would look like with
      the above way of doing this? Once element.event("change"); is
      called via waitUpTo, is it as simple as running the change event on
      said element and returning the result (whatever that may be, in this case
      the original element).

There was also the possibility of:

element.on("change");

As the API. I guess this relates to the previous question, I was taking a
look at this option and was wondering how this approach would look.

public interface Evented {
Event on(String eventName);
}

Just something like that? I guess thinking about it that's more of a
naming concern if it may do the same thing implementation wise.

What would the EventDescriptor look like? Some sort of wrapper for the
common events?

Some of this discussion may go in the other issue I'm assigned so I can
move that over there if it seems more relevant.


Reply to this email directly or view it on GitHub
#39 (comment)
.

So then in this, we want Evented (or whatever the name may be, Evented/HasEvents) to be extended by the Element interface in darcy-ui therefore all elements have the API? Or did we want to put this interface in darcy-ui then leave the implementations dealing with various DOM events to HtmlElement?

For now, no. I think we should treat the interface as a role based interfaced that implementation implement if they support events. If you need event support in your page object, you cast your element to Evented and deal with the consequences if that impl does implement that interface.

You mentioned: Each element type will have to override unfortunately so that Event is typed with the more specific element.

MyButtonImpl implements Button, Evented<Button>

That way new MyButtonImpl().event("change") returns an Event<Button> which means

Button myButton = new MyButtonImpl(); 
assertSame(myButton, myButton.event("change").waitUpTo(1, SECONDS));

But this get's to a large discussion about what the Event should return, and I don't think it should be that.

element.on("change");

on in many frameworks (jQuery, etc.) is used to bind event handlers. We aren't really doing that in this case (there is no callback to pass). Maybe we could do that one day. In our case, we are concerned exclusively about this scenario:

Synq.expect(element.event("change")).waitUpTo(1, SECONDS);

In which case what we are expected is an event to occur so event reads better than on. I'm still open to other options.

What should event return?

In Javascript, event callbacks accept an event object that has various properties about the event, in addition to the target of the event. I think we should follow this pattern. This means something like:

interface Evented {
  Event<EventDescription> event(String eventName);
}

interface EventDescription {
  // possible metadata about the event?
  Element target();
}

Notice that target() returns an Element. What if our target is a button? ...

interface EventDescription<T> {
  T target();
}

interface Evented<T> {
  Event<EventDescription<T>> event(String eventName):
}

Now target() will return whatever we type our Evented implementation with, which will be per element and so can be implemented generically, allowing type safe targets.

EventDescription

What else can go in this interface? In Javascript, the properties of an event change per event type: https://developer.mozilla.org/en-US/docs/Web/API/Event . However, if we wanted different events to return different EventDescription interfaces, then event names could be Strings, they would have to be their own type, and that type itself would have to be generically typed with a subclass of EventDescription. Yowsa.

// In darcy-ui
interface EventType<T extends EventDescription<U>, U> {
  String getName();
  T getDescription();
}

interface Evented<T> {
  <U extends EventDescription<T>> Event<U> event(EventType<U> eventType);
}

// In something lower level
class MyTextInput implements TextInput, Evented<TextInput> {
  // ...
}

class ChangeEvent implements EventType<ChangeEventDescription<T>, T extends Element> {
  // ...
}

class ChangeEventDescription<T extends TextInput> {
  // Hypothetical API
  public String oldValue() { /* ... */ }
  public String newValue() { /* .. */}
  @Override public TextInput target() { /* ... */ }
}

// Usage
TextInput myInput = new MyTextInput();
Event<ChangeEventDescription<TextInput>> inputToChange = myInput.event(new ChangeEvent());

ChangeEventDescription<TextInput> eventDesc = inputToChange.waitUpTo(10, SECONDS);
assertSame(myInput, eventDesc.target());

So obviously that is pretty intense. This is why I'm not entirely sure what to do here.

In the meantime, what we really want from this is to be able to wait for mutations in darcy-web, and maybe we can just side-step the higher level darcy-ui Evented interface (and its associated complication) and implement the more specific mutation use case in darcy-web.