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

Added initial Angular content #47

Merged
merged 9 commits into from
Aug 5, 2018
Merged

Added initial Angular content #47

merged 9 commits into from
Aug 5, 2018

Conversation

alterx
Copy link
Contributor

@alterx alterx commented Jul 27, 2018

This PR accompanies chromaui/learnstorybook-code#4 and addreses #2

What's inside:

  • English version

What's missing:

  • Spanish version
  • Update commit SHAs for all the pages after that PR gets merged
  • Proofread the whole thing (I could use some help with this one 😄)
  • 🚀

Copy link
Member

@domyen domyen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I proofread the English version. There are a few minor copy changes but it all looks great! 👍


We’ve now successfully built out a component without needing a server or running the entire frontend application. The next step is to build out the remaining Taskbox components one by one in a similar fashion.

As you can see, getting started building components in in isolation is easy and fast. We can expect to produce a higher-quality UI with less bugs and more polish because it’s possible to dig in and test every possible state.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: Double "in" here


# Assemble a composite component

Last chapter we built our first component; this chapter extends what we learned to build TaskListComponent, a list of Tasks. Let’s combine components together and see what happens when more complexity is introduced.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to "a list of TaskComponents"

Our component is still rough but now we have an idea of the stories to work toward. You might be thinking that the `.list-items` wrapper is overly simplistic. You're right – in most cases we wouldn’t create a new component just to add a wrapper. But the **real complexity** of `TaskListComponent` is revealed in the edge cases `withPinnedTasks`, `loading`, and `empty`.

```javascript
iimport { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iimport --> import


Note that we’ve been able to reuse the `withPinnedTasks` list of tasks in both story and unit test; in this way we can continue to leverage an existing resource (the examples that represent interesting configurations of a component) in more and more ways.

Notice as well that this test is quite brittle. It's possible that as the project matures, and the exact implementation of the `Task` changes --perhaps using a different classname or a `textarea` rather than an `input`--the test will fail, and need to be updated. This is not necessarily a problem, but rather an indication to be careful liberally using unit tests for UI. They're not easy to maintain. Instead rely on visual, snapshot, and visual regression (see [testing chapter](/test/)) tests where possible.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Task --> TaskComponent

};
});
```
We see that although the `default` story works just fine (because we have default data in our store), we have an issue in the `error` story, because the `error` field is connected to the store itself and we can't just modify it from outside. We need to dispatch and action on the store.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"...dispatch and action" ---> "...dispatch an action"

Import Chromatic in your `.storybook/config.js` file.

```javascript
import { configure } from '@storybook/react';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/react ---> /angular

@domyen
Copy link
Member

domyen commented Jul 28, 2018

Can you add link to the Angular English and Spanish editions in these locations:

  • components/Header.js
  • pages/index.js

Copy link
Contributor

@icarlossz icarlossz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can help with the spanish translation.

@alterx
Copy link
Contributor Author

alterx commented Jul 30, 2018

@icarlossz, well, I've already started with that one. As soon as I get a first draft I'll submit it and you can proofread it. Does that work? 😄

@icarlossz
Copy link
Contributor

@alterx It sounds great to me!

@alterx
Copy link
Contributor Author

alterx commented Jul 31, 2018

@icarlossz I've added the Spanish version
@domyen I've addressed your change requests (and a couple more I found)

import 'storybook-chromatic/storybook-addon';
import '../src/styles.less';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should remain .css. The user is not actually running less, rather they're including the pre-compiled .css file included in the repo.

Copy link
Contributor Author

@alterx alterx Jul 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I changed that in both the code and in the tutorial. I've configured the cli to use LESS. It didn't really make sense to use gulp and compile less when the @angular/cli + webpack can allow use of .less files.

You can see the commit here: chromaui/learnstorybook-code@c15b751

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sgtm thanks for the explanation 👍

Copy link
Contributor

@icarlossz icarlossz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed, the spanish translation is good.

Copy link
Member

@tmeasday tmeasday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so great! Can't wait to get it out!!

Two things:

  1. We can add syntax highlighting for typescript and json by changing Highlight.js to include loadLanguages(['bash', 'typescript', 'json']);. Looks nice!

  2. I wonder why you decided not to do the PureTaskList and demonstrate the pattern of splitting the presentational part of a container out for its stories?

Is this difficult to do in angular? I feel like it is a useful trick, and without it the data chapter feels a bit weird, like there is no lesson in it, it is just setting up the next chapter.

```javascript
{
"scripts": {
"build-storybook": "build-storybook -c .storybook -o .storybook-static"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove -o .storybook-static -- I just made this change to master

@alterx
Copy link
Contributor Author

alterx commented Aug 1, 2018

Hey @tmeasday, regarding the PureTaskList thing, I'm not really sure it makes sense for Angular. Let me try to rationalize it:

In React, when we use connect we compose a new higher order component that will provide the "selected" data to the underlying "presentational" component. Both components exist on their own and connect just enhances what the presentational component can do.

In Angular, in order to create a "connected" component we just inject the store and then we can select the data inside. It felt a bit unnecessary to add that part since we already have a presentational component (TaskList) and we'll build a "container" (InboxScreenComponent). So, if I had included that PureTaskList the TaskList would've basically been the same thing as the InboxScreenComponent and we weren't going to be able to just import "the presentational part" of the component.

In other words, there's no similar way to have the PureTaskList and TaskList in the same file and connect one while leaving the other one as a pure component like React allows. The only way would be to have two different component definitions: one for the dummy PureTaskList and one for the "connected" TaskList that would basically include a PureTaskList and a reference to the store, which is exactly how the relation between InboxScreenComponent and TaskList looks like.

Because of that, it felt weird to add it. It's basically replicating the same thing twice. I don't know if this is clear enough, tho. Let me know if you get what I'm trying to say.

I do agree that the data section feels a bit out of place on its own, and I actually thought about unifying both parts but didn't want to make a change that big to the structure of the lessons.

@tmeasday
Copy link
Member

tmeasday commented Aug 1, 2018

@alterx -- thanks for the write up, that makes perfect sense to me.

I feel like the issue involved here is simply that it isn't convenient to define two components in the one file in Angular as it is in React. Otherwise everything you've said above applies to React as well, and I struggled as well with the fact that InboxScreen doesn't really do much above the containerised TaskList.

Here's what I was thinking and I'll let you decide if it is worth the awkwardness of defining another component:

  • It is a common pattern to include container components deep in the hierarchy of your application (as opposed to only at the top-level, e.g. InboxScreen).
  • A useful pattern to allow you easily write stories for container components is to export/split out the presentational component inside and target the stories at that. (This is the lesson that we are trying to impart in the data chapter).
  • This works great at the level of that container, but you cannot apply that technique to any components higher up the tree because they include the container.
  • A useful pattern to deal with writing stories for "container-containing" components is to mock out the data source. (This is the lesson that we are trying to impart in the screens chapter).

Although in this example app it really doesn't make a lot of sense to have the container not at the top-level, in order to try and demonstrate both of the above I made things a bit convoluted and showed errors at the screen level, but fetch the data at the TaskList level.

I realise you wouldn't write an app like this in reality but things are typically way more complex and you are often tempted to embed containers deep in the hierarchy. So these techniques are good to know!

(As we mention in the screen chapter it is possible to avoid embedded containers completely in your app and not ever do the data-mocking technique, but that's not a good fit for a lot of (esp. redux) apps).


I wonder: if splitting the presentational part from the data access in a container is unnatural in Angular, is there a way to do the first technique in some other way? I feel like it's a good idea for people to know about!

@alterx
Copy link
Contributor Author

alterx commented Aug 1, 2018

Exactly!, I was struggling to see the need for the two components even in the React app but now that you explain why I get it and it makes sense. The main difference is that, in Angular, that separation of components isn't as "convenient" and felt even more awkward without knowing the context.

The way of doing this in Angular is actually creating both components. I think I could add that and clarify a bit the message 😄

@tmeasday
Copy link
Member

tmeasday commented Aug 1, 2018

Awesome!

I think maybe I need to work on the text a bit to make the above reasoning a bit more obvious so people aren't thinking "this is weird why would I ever do this" ;)

Copy link
Contributor

@icarlossz icarlossz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!

@alterx
Copy link
Contributor Author

alterx commented Aug 1, 2018

@tmeasday

So, after a bit of thinking, I'm still not sure this part is really useful in the Angular app. In Angular, as in React, we do have connected components all over the component tree (not only at the top level) but the component definition mechanism and the separation between dumb-smart components works a bit different and the way components are bootstrapped removes some of the issues this section tries to solve.

Here's what I was thinking and I'll let you decide if it is worth the awkwardness of defining another component:

  • It is a common pattern to include container components deep in the hierarchy of your application (as opposed to only at the top-level, e.g. InboxScreen).
  • A useful pattern to allow you easily write stories for container components is to export/split out the presentational component inside and target the stories at that. (This is the lesson that we are trying to impart in the data chapter).
  • This works great at the level of that container, but you cannot apply that technique to any components higher up the tree because they include the container.
  • A useful pattern to deal with writing stories for "container-containing" components is to mock out the data source. (This is the lesson that we are trying to impart in the screens chapter).
  1. is also true in Angular.
  2. this is the one I have more issues with since it doesn't work/feel the same way in Angular. When we talk about "presentational components" in Angular we just have a plain old component that only has @Input() and @Output() fields. It doesn't perform any other tasks outside of processing the inputs, rendering and, if necessary, sending out events. A connected component just gets a store via dependency injection and does it's thing, that's the main difference. So, splitting the presentational component out basically means leaving the TaskListComponent as is and create a new "connected" component that renders TaskListComponent and provides it with the task list.
  3. with Angular that isn't a problem: as long as you've injected the right thing via the providers array and the component is included in the ngModule you'll be able to render all the things, I've created an example in a separate branch: alterx/learnstorybook-code@74df8a2
    As you can see, other than the error related code, there's not much difference between the InboxScreenComponent and the ConnectedTaskListComponent (and their .stories.ts files) and rendering the stories for InboxScreenComponent even though it includes the ConnectedTaskListComponent is trivial as long as the rightproviders and components are available.
  4. In this case we "mock" it by injecting the real thing and it "Just Works™" (again, this is due to Angular's DI system).

After this explanation and reviewing the code I've added to that branch do you feel like it makes sense to add it to this PR? I just can't shake the feeling that we'd just be repeating ourselves here

@tmeasday
Copy link
Member

tmeasday commented Aug 4, 2018

Hey @alterx thanks again for bearing with me and explaining things to me, I appreciate it.

I'm still not sure things are so different between Angular and React--notice that we could also achieve an equivalent thing to your ConnectedTaskListComponent's story in React/Redux with something like (linked):

  // This Angular:
  .addDecorator(
     moduleMetadata({
       declarations: [],
       imports: [TaskModule, NgxsModule.forRoot([TasksState])],
       providers: [Store],
     }),
   )

  // Is equivalent to this React:
  import { Provider } from 'react-redux';
  import store from '../lib/redux';

  .addDecorator((story) => (
     <Provider store={store}>{story()}</Provider>
  );

(I think anyway, apologies if I am getting this wrong).

So, again, I'm not sure things are all that different, I think I still just haven't explained why I did it this way well enough ;). Let me have another try:

  1. Note that we don't write a story for the connected component in learn storybook, so in some ways the awkwardness of ConnectedTaskList and InboxScreen being the same is not so apparent 😊

  2. Not that in the code snippet above, we are just letting the default state of the store render. I'm not sure that makes a good story -- really we should be providing starting state as part of the story so it is clearer what's going on. I'm sure that's possible but it's a little added complexity for the "injecting/context" approach.

  3. In the case of more complex tools (or real redux stores), it may be a lot more complex. For instance, I am used to Apollo/GraphQL, where again it is possible to mock the results of a query for a Query component, but it is much more brittle than providing the props directly and definitely a order of magnitude more complex than the "split into connected/presentational, write stories for presentational" approach.

For this reason, I really think it's good to show people that technique, definitely for React where the splitting is pretty easy and natural and probably advisable for other reasons too. For Angular, where it is awkward, I get why you aren't keen to do it.

I'm not sure. I still think the technique may be useful for Angular people, and I would suggest we show it to them even if it doesn't really make sense for this particular app. But I am by no means an Angular expert (or even user) so I will completely defer to your instincts on this. Let me know what you want to do!

@alterx
Copy link
Contributor Author

alterx commented Aug 4, 2018

Yeah, I think my main issue is that we're basically creating two components that are fundamentally the same thing (TaskListComponent becomes PureTaskListComponent, then we create a new ConnectedTaskListComponent then, in the next chapter, we create an InboxScreenComponent that is almost identical to the ConnectedTaskListComponent. It feels like duplicating things for the sake of showing a complex pattern that may not be apparent to a person that's new to the framework and/or Storybook. So it boils down to the example being so contrived and my mind feeling like these components are just too much haha, but then again, I know that we should intentionally keep things simple.

I'll add the new components.

@alterx
Copy link
Contributor Author

alterx commented Aug 4, 2018

@tmeasday , I've added the new text in english, please take a look and tell me what you think.

I had to change the "We see that although the error story works just fine, we have an issue in the default story, because the TaskList has no Redux store to connect to. (You also would encounter similar problems when trying to test the PureInboxScreen with a unit test)." part in screen.md because in Angular the fact that the PureInboxScreenComponent includes the TaskListComponent requires all the dependencies of the latter to also be included. In other words, PureInboxScreenComponent is not really pure so even if I submit the error prop I also need to submit all the other properties and providers (the store, for example).

This is one of the things I thought wouldn't make a lot of sense to add (the PureInboxScreenComponent) but I added it for the sake of completeness when comparing this to its React counterpart but there's no difference between having the PureInboxScreenCompoment along with the InboxScreenComponent and only having the latter. Either way we still need to inject all the dependencies to make the stories work.

Please let me know if the text makes sense and conveys what you wanted to convey in the React text so I can translate the missing parts to spanish.

@alterx
Copy link
Contributor Author

alterx commented Aug 5, 2018

I think this last commit solves the whole thing while showing an alternate way of defining stories :)

@tmeasday
Copy link
Member

tmeasday commented Aug 5, 2018

I think this is great @alterx! Let's get this merged!

@tmeasday tmeasday merged commit e46abb1 into chromaui:master Aug 5, 2018
@tmeasday
Copy link
Member

tmeasday commented Aug 5, 2018

We probably still need to fix up the commit links .. I can do in the morning..

@domyen domyen mentioned this pull request Aug 6, 2018
jonniebigodes pushed a commit that referenced this pull request Jun 17, 2022
* Update: Introduction revise

* ✍️ Update: create an addon > decorators 번역 교정

* ✍️ Update: create an addon > getting started 번역 교정

* ✍️ Update: create an addon > getting started 번역 교정

* ✍️ Update: create an addon > introduction 번역 교정

* ✍️ Update: create an addon > preset 번역 교정

* ✍️ Update: create an addon > register addon 번역 교정

* ✍️ Update: create an addon > track state 번역 교정

* ✍️ Update: design systems for developers > distribute 번역 교정

* ✍️ Update: design systems for developers > architecture 번역 교정

* ✍️ Update: design systems for developers > build 번역 교정

* ✍️ Update: design systems for developers > conclusion 번역 교정

* ✍️ Update: design systems for developers > document 번역 교정

* ✍️ Update: design systems for developers > introduction 번역 교정

* ✍️ Update: design systems for developers > reivew 번역 교정

* ✍️ Update: design systems for developers > test 번역 교정

* ✍️ Update: design systems for developers > workflow 번역 교정

* ✍️ Update: ui testing handbook > conclusion 번역 교정

* ✍️ Update: ui testing handbook > automate 번역 교정

* ✍️ Update: ui testing handbook > introduction 번역 교정

* ✍️ Update: ui testing handbook > workflow 번역 교정

* ✍️ Update: ui testing handbook > accessibility testing 번역 교정

* ✍️ Update: ui testing handbook > composition testing 번역 교정

* ✍️ Update: ui testing handbook > visual testing 번역 교정

* ✍️ Update: ui testing handbook > interaction testing 번역 교정

* ✍️ Update: ui testing handbook > user flow testing 번역 교정

* ✍️ Update: visual testing handbook > workflow 번역 교정

* ✍️ Update: visual testing handbook > vtdd 번역 교정

* ✍️ Update: visual testing handbook > introduction 번역 교정

* ✍️ Update: visual testing handbook > automate 번역 교정

* ✍️ Update: visual testing handbook > conclusion 번역 교정

* ✍️ Update: visual testing handbook > component explorer 번역 교정

* ✍️ Update: intro to storybook > composite component 번역 교정

* ✍️ Update: intro to storybook > conclusion 번역 교정

* ✍️ Update: intro to storybook > contribute 번역 교정

* ✍️ Update: intro to storybook > data 번역 교정

* ✍️ Update: intro to storybook > deploy 번역 교정

* ✍️ Update: intro to storybook > get started 번역 교정

* ✍️ Update: intro to storybook > screen 번역 교정

* ✍️ Update: intro to storybook > simple component 번역 교정

* ✍️ Update: intro to storybook > test 번역 교정

* ✍️ Update: intro to storybook > using addons 번역 교정
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants