 So, hi everyone. I'm Jew and today I'm going to talk about how to design reusable components in number two. Well, first thing I have to say is, like, that's really not true because number two just came out today, so. But I really needed to find a good title for the talk, so I just went for it. The actual story is, like, how we build a calendar component in number 112, but that's not actually true either because we plan to build a calendar component in number 112, but since Amber is quite a fast-moving target, when we actually got to the step of actually building the component, Amber 113 was already out there, which I think is quite good because probably we'll work in number two as well, hopefully. So, I lied quite a lot in, like, 45 seconds of the talk, but we tried to do the things in the Amber two way, and the Amber two way, we just, like, figure out by reading articles and blogs and RFCs and stuff like that. So, hopefully some of the lessons we've learned along the way will be interesting. So, what do we build? It's a calendar, so I'm sure everybody is pretty familiar with Google Calendar. You click, add occurrences, you can resize occurrences, you can drag and drop occurrences. As a software developer, I'm sure that you know, like, what are our nemesis, like, in our craft, which are naming things, cache invalidation, time zones, something else. Off-wind line errors. Well, time zone support. So, one of the things we had to build in the calendar was time zone support, which was interesting. I think Eugenio spent something like three, four weeks, months into making time zone support, like, work. But in the end, we got to the point where we had Amber Calendar with time zone support, where you could search for time zones, you could set a time zone. It would actually work, which was amazing. But why? So, I think right now, with Amber two, the right way to go is to switch to components. But when we started, we didn't really know that. Or maybe we had an intuition that the direction was that one. But the main reason we did that is because we needed this calendar in a lot of different places. So, we have a lot of apps which need some sort of calendar. Their needs are a bit different. So, we thought, well, we might just as well extract it in a shared component and use that in all the separate apps. And that was the initial idea because the first time, we just built two different calendars. And at some point, it's like, okay, I need this on the other app as well. And just felt wrong. So, the actual business is connecting people together with a phone call most of the time. So, we have four offices in four different time zones. So, time zone support is quite important, especially because otherwise, we schedule a call with some guy in the United States. And he just forgets to show up or stuff like that, which is unfortunate. So, I don't know if you can see quite a lot, but this is one of our internal apps which our colleagues use for scheduling calls. And this fully built on Ember. And here is the calendar in action. This is one of the apps. And this is another app that the clients are using. And even this one is using the calendar. Can I get a glass of water? Yeah, of course. Okay, demo time. So, we have the calendar here. So, as I said, you click at an event, click at another event. You can resize events. You can take and drag and drop events around. You can navigate over stuff, add more stuff, jump back to your week. The time zone support works. So, in Europe, London. And for example, you could want to switch to New York. And all the events just like go up. If these are like where our main offices are. So, that's the default query for showing time zones. But if you wanted to search for a different time zone, just like pop up, you could choose it and the events would adjust accordingly. So, yeah, time zone works. Good. Something I'm quite proud personally is building this drag and drop. Because if you notice, it also works when you're like going around the page. Well, not anymore. Obviously, it broke it. And also, it's a bit smart because it tries to be inertial. So, if you start dragging at the bottom of the event and you go up, then it will start dragging again only when you get to that point and stuff like that. It's a lot of manipulation. But enough about that. So, let's talk a bit about the general architecture of the thing. So, this is the calendar. You call it, you pass a list of occurrences. And this is the data that the calendar is using to populate all the events. And then you specify what you want the calendar to do whenever one of these user interactions happen. So, whenever someone clicks, the calendar will call the action that you have passed, passing you some data along with it. Whenever someone tries to do update some of the occurrences, we'll call the calendar update occurrence. Whenever someone tries to remove an occurrence, we'll try to call the calendar remove occurrence. I think it's quite short, this example, but it's quite interesting because how many of you is familiar with the data down actions up paradigm? Good. So, basically, it means that whenever you're using a component, you should pass data down to this component. And this component should take this data and treat it as holy. It should never touch it, should never modify it, well, to a certain extent. But instead, whenever a user interaction happens, you should call an action and send it up to the upper context. And the upper context really knows what needs to be done. So, in this example, let's say we have on add occurrence, and the calendar will know that whenever a user clicks on the grid, you will send up this action, which is calendar add occurrence. So, whenever we write this template, that's the context that we're using. And therefore, in the controller, or maybe it should be like another component, we should define this action, which is calendar add occurrence, where we get this occurrence, we get all the occurrences, we validate the thing that we're trying to add, and if the validation succeeds, then we actually add the occurrence. I think this is, at least when we started doing this, it was very much the way to go. And I think number two is more than so. I think it's quite the suggested way to go to build components. And it's called data down actions up because of this reason, so you pass data down. And it's especially evident once you have a structure of nested components. So, the data is always passed from the top-level component down, down, down. And from the, like, last, from the last level of the components, you send actions up to the top-level context. The advantage of doing this is, first of all, you know, whenever you, even if you have a huge structure of nested components, that only the top level is actually mutating data. So, you don't have those nasty bugs because some of the small components is touching something, and some other small components touching something else. And then you don't know really who's controlling state. As a funny thing, we actually, whenever you're doing occurrences, myoccurrences, in the, in this Ember component, we actually proxy the data internally. Because at that time, whenever you're passing variables to a component, there was a two-way data binding. I think in number two now, this has, it's not the case anymore. You have to explicitly say that you want the two-way binding to happen with a mute, right? Or that's two-one. Okay. Okay. So, what we do is, just like to avoid that, we proxy, we have like an internal occurrence object. We just proxies your real objects. And the component always sends the actions up, and we always let the upper context decide. Because the upper context really knows what to do. And one of the advantages that we found is, for example, all the validation logic does not need to belong to the calendar. The calendar is quite dumb. It just knows, okay, someone clicked there. But you're right, you're using this add-on. And you know what is the business logic that you want to apply. So, you know whenever something is valid, something is invalid. And we do the same thing, for example, when people try to drag one event on top of one another. Because there might be applications where you can actually have overlapping events. And there might be other applications where you shouldn't have overlapping events. But it's not a responsibility of the component to know that. Okay. Obviously, since we're trying to use this component in multiple applications, we want to customize the behavior. So, we can't really use this all the time. Because sometimes we just want to do something different. The way of customizing this is by passing a block to the component. And in our component, we pass variables within the block, the event, the grid, and the actual calendar component. So, you can use these variables to access information if you want to. And this block has a default block. This component has a default block, which just calls an acid component, which is called timetable occurrence. And it gets the occurrence. It gets some information like how high the time slot should be, how long is that time slot. It passes the grid. So, the timetable know actually where to draw stuff. And also, the nested component has some actions, which then call the parent component. And this is the default block. So, whenever you're using the simple syntax, the calendar actually uses this. But, for example, we have another app where we have a lot of editable events and fixed events. So, you should be able to change all the editable events, but you shouldn't be able to change the fixed events. So, for example, now here we're doing, we're checking if the actual occurrence is editable. And if it is, we're using the smart version of occurrence, which is called timetable occurrence. If it's not, we're just using the dumbed version, which is called occurrence. So, basically, in this way, we can manage this calendar with a lot of fixed events and a lot of editable events. Testing. Yes. Testing. When we started testing this, we followed the amber practice of unit testing components. I'm not sure how many of you actually tried, but it's a huge pain. You need to define all the dependencies and then do this sort of weird stopping of things. And at the end, you work three days writing this test, and you try to read them from top to bottom. You can't really understand anything. You're like, I don't know what this thing is doing. And it's especially frustrating when the test fails at some point. I think the new way, number two, of doing things is, has a weird name, but it's pretty sweet. The weird name is component integration unit test. It's still a unit test, so you go, you look in the folder like test unit and component stuff. But actually, it's an integration test, so you can click, you can fill fields, you can search stuff, you can actually do things. It's pretty amazing. The way to enable this is whenever you're writing a test, you pass this integration through. I've just seen today, there's this deprecation notice which says that this is going to be the default behavior from at some point. And if you actually want a true unit test, you have to pass unit through. So I think that's a good like symptom that this is the way to go. And this is, I really like this. Basically, there's this HBS string decorator, and you can render your particular version of your component. You can pass all the options that you want, and the test will take your component, will run using these variables, this information, this data that you pass in, and then you can make assertions on this specific version of the calendar, which is nice because there's, since this is quite a big component, there's a lot of options. And so I don't want to have a huge test which I can't really understand. In this way, you can have 10 different tests, and in each one of the tests can just test one single thing. Something that's also really nice is that here, if you see, we have occurrences and calendar add occurrence. But since the actual component doesn't store data and doesn't even really know what to do when calendar add occurrence happens, you have to define these things on the test itself. So you set the data there, we just set occurrences to a number array, and we tell the test what to do whenever he receives the action calendar add occurrence. So in this case, just take all the occurrence and we just add it to the list. And then we have the actual test. So we select the time we created these helpers to be able to select the first time slot of this first day, and then we check that actually one occurrence has been added to the calendar. Since we want to use this component in a lot of apps, we don't really want to repeat the work that was like the really painful work that we had to do to make all those helpers work. So the component also exposes these test helpers. I think my voice went out in rightly the exact time. That's it, thank you. So actually, me and Eugenio built this thing. So I'm sure that Eugenio is available to answer all the questions that you have. If you have any questions, but please do, yes. By default, if you don't set that, by default, whenever you're calling this, Amber will try to, okay, I want to load AS calendar. So we'll try to find the template for it. You will see that within the template, you're using other components. So we'll tell you, I have no idea what you're talking about. So usually you need to set a huge list of all the dependencies for this component. So you say, I need this, I need moment, I need this other component, I need this list of six helpers I'm using my views. If you set integration true, you won't have to do any of that. All that kind of stuff is automatic load, it is loaded automatically for you. Yes, which is, it's real, I mean it's good if the components really is small, but if you have a deep nested structure of components, just like crazy, because you have to specify all the possible dependencies of every nested component, which is, yeah. Is it on GitHub? Yes, it's on GitHub. So, well I don't have internet, but it's on AlphaSites, Amber Calendar. So, yes, yes. By default there are some styles which have shown you, which require like our own CSS framework, but they're not required at all. So you can write your own CSS if you're brave enough. Yes. Can you just go back to your slide on the testing? Sure. Which one? Yeah, that one. Yes. You're a server equal, there's a lovely helper, I've been helping upgrade to number two, expectElement and expectComponent. It's a expectElement, stop as kind of reference for a while. Oh, okay. Oh, okay, yeah, that's nice. One thing I forgot to mention is that actually in these component integration tests you can't really use the integration helpers. So you still need to like fetch the element in jQuery and call click on it and stuff like that, which is not really great, but I still think it's okay. At least you can still read the code and the code makes some sense. So I like that. I was just going to mention the pre-processing of handlebars in line is a, it's called a tagged template string. Tag template string. Tag template string and that's a proposed spec for ES7 I think, so that'll be a ground native thing, but right now this is implemented as a like Babel plugin, Babel plugin that talks to the HTML bars compiler. So it's quite fun. If you use this in one of your integration tests and then open up open up the compiled source in the browser, you'll see what compiles HTML bar template looks looks like sat there right in the middle of your test. It's not as hairy as you think. Also makes my Vim highlighter just go completely insane. There's quite a lot of forms that are unique and so it seems like the younger way is supposed to pass like a lot of the form that occurs into each of the inputs and do a whole heap of manual templating, which is particularly good, because basically actions don't bubble up the DOM, they bubble straight to the rooter. I think we went like both ways at some point, like at some point we're just like doing stuff like automatically inferring the parent component from a messy component. There is a way of doing that, but after some time doing that we just like felt like it was too painful and we just like started passing it explicitly. So for example all the input components would just always pass the form in. At some point it's just better because you are much like you have much more freedom in how you want to nest the component, so you don't have to infer it from some certain magical structure that you have in the code. I don't like it, but I think that's like the best way we've found. Yeah, exactly, exactly. There are some dirty tricks that you can do, I think. The way that we've done it is just create more components. So if you have this table, then create a component which is called table header, table footer, table content, table footer, and maybe sometimes you still have some sensible defaults, but whenever you want to customize only the header, then you just repeat the whole structure. I don't think it's great, but still better than the dirty hacks that we've found. Oh, okay.