-
Notifications
You must be signed in to change notification settings - Fork 4k
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
feat(List): update to v1 API #311
Conversation
Current coverage is 88.81% (diff: 78.57%)@@ master #311 diff @@
==========================================
Files 66 68 +2
Lines 892 894 +2
Methods 0 0
Messages 0 0
Branches 0 0
==========================================
+ Hits 792 794 +2
Misses 100 100
Partials 0 0
|
</div> | ||
) | ||
} | ||
function _List(props) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is your first PR, I'll do a little extra explaining 😉 This is an inline comment, you can leave these by clicking a line number in the diff (use the File tab).
When a comment is left on a specific line like this, it is because we want to discuss the code on this line or just surrounding it. Here goes.
Underscore names are reserved for private components and variables. Let's rename this to List
instead.
-function _List(props) {
+function List(props) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does _Header.js have an underscore? Is it private?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. It is a base header component to be reused among the various header components.
On a side note, the header subcomponents do not really follow our API pattern and should be rethought.
…chine and also all the examples appear to work.
const rest = getUnhandledProps(_List, props) | ||
|
||
return ( | ||
<ListComponent className={classes} {...rest}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the ListComponent
never changes, let's use a div
inline here instead of creating the assignment:
-const ListComponent = 'div'
const rest = getUnhandledProps(List, props)
return (
- <ListComponent className={classes} {...rest}>
+ <div className={classes} {...rest}>
{children}
- </ListComponent>
+ </div>
The next big step for this component is to turn the supported SUI HTML classes into a React Component API. I would reference the work done in #268 which updated the Divider to our v1 API. This is mostly centered around creating declarative props for component features. Then, using our We have an open issue to add documentation around this process. I'll go ahead and start that right now. |
Please let me know if this last commit was the direction you wanted to take. @levithomason |
.should.have.tagName('div') | ||
}) | ||
|
||
it('adds the "list" class', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is covered by the common.isConformant()
test, all components are required to have their component name as a className. Let's delete this test.
I'm almost done with the docs for writing components, though it looks like you've just about got it down!
size, | ||
useValueAndKey(aligned, 'aligned'), | ||
useKeyOnly(bulleted, 'bulleted'), | ||
useKeyOnly(link, 'link'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the link
class is used, the ListItem
s need to use an a
tag:
<div class="ui link list">
<div class="active item">Home</div>
<a class="item">About</a>
<a class="item">Jobs</a>
<a class="item">Team</a>
</div>
I've considered adding a component
prop to every component, which would allow setting the component to render as. In this case, an a
. When a List
has link
set, we could map all the ListItem
children and set it like so <List.Item component='a' />
.
Note the active
item always uses a div
. So, a <List.Item active />
should always render a div
.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea. However, which other use cases would this come in handy? If we can't identify other use cases then is there a way to do away with component
and make this the default for non-active link-list items? I also worry that the user may leave out component
when it is required or use it when it shouldn't be used. It just has the potential to lead to more bugs and confusion for end-users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the latest on component augmentation (component
/elementType
/as
prop) see https://github.com/TechnologyAdvice/stardust/issues/403.
For more on the link
prop and rendering as an a
tag see https://github.com/TechnologyAdvice/stardust/pull/382#discussion_r75187539.
List API ProposalThe updates made to the ListItem
The current In order to support our latest guidelines for markup, we need to consider using sub components for these component parts. Here's why. Props for component parts The advantage to using props is that the JSX is minimal: <List.Item icon='user' description='John Doe' /> The downside here is that any list item markup beyond that of plain text is awkward to work with. What if you want a header link with an Sub components for component parts The advantage to using sub components is that it is flexible. This example adds all the difficult markup I just mentioned above: <List.Item>
<Icon name='user' data-foo='bar'>
<List.Content>
<List.Header href='johndoe.com'>
John Doe
</List.Header>
<List.Description>
<p>John Doe is <b>so</b> awesome.</p>
</List.Description>
</List.Content>
</List.Item> The downside being that it is very verbose. However, if you need flexibility then this is acceptable. Supporting both I'd like to support both of these. This should be rather simple, actually. If you have simple markup you can provide If you have intricate markup you may use We have a The following examples show both APIs I'm proposing. Simple Item<List.Item description='Apples' />
// or
<List.Item>Apples</List.Item> <div class="item">Apples</div> Icon or ImageMarkup adjacent to an <List.Item icon='user' description='Semantic UI' />
// or
<List.Item>
<List.ItemIcon name='user' />
<List.ItemContent>
Semantic UI
</List.ItemContent>
</List.Item> <div class="item">
<i class="users icon"></i>
<div class="content">
Semantic UI
</div>
</div> Header & DescriptionSince the description here is more than just plain text, the prop API is not preferred. I've shown it anyway. const description = <span>Click a link in our <a>description</a>.</span>
<List.Item header='Header' description={description} />
// or
<List.Item>
<List.ItemHeader>
Header
</List.ItemHeader>
<List.ItemDescription>
Click a link in our <a>description</a>.
</List.ItemDescription>
</List.Item> <div class="item">
<a class="header">Header</a>
<div class="description">
Click a link in our <a>description</a>.
</div>
</div> Image, Header & DescriptionThe example here assumes the const description = (
<span>Last seen watching <a><b>Arrested Development</b></a> just now.</span>
)
<List.Item
image='daniel.jpg'
header='Daniel Louise'
description={description}
/>
// or
<List.Item>
<List.ItemImage src='daniel.jpg' />
<List.ItemHeader>Daniel Louise</List.ItemHeader>
<List.ItemDescription>
Last seen watching <a><b>Arrested Development</b></a> just now.
</List.ItemDescription>
</List.Item> <div class="ui list">
<div class="item">
<img class="ui avatar image" src="daniel.jpg">
<div class="content">
<a class="header">Daniel Louise</a>
<div class="description">Last seen watching <a><b>Arrested Development</b></a> just now.</div>
</div>
</div>
</div> You can see the difficulties in using the props API for markup. Also, the verboseness but flexibility of the sub component API. Looking forward to your thoughts. |
@levithomason I like this proposal very much. I think the cleanliness of sub-components makes the API more flexible and extensible for end users. I believe there will be ample instances where a user will want to enter a long description, and perhaps even include other components in a description. Passing everything through props alone would be very restrictive and cumbersome in that regard. That said, providing the shorthand format too is an excellent alternative for those opting out of unnecessary verbosity in their code. I think this would be a great step forward for maximizing the usability of our list components. |
Although I can create ListItem, ListHeader, etc. as subcomponents in the same folder as List (as seems to be described in the doc) this will result in a lot of duplication esp. with the Header component which has 9 files by itself (H1, H2, etc. Subheader). Do you still suggest these as duplicate components under the List directory? |
Take my last commit with a grain of salt. I'm not positive I took this in the direction you had in mind. The new commit does let you create list items both via sub-components and non-sub which was the primary goal. I also create the content and description elements which I'm not positive you wanted separated as reusable elements; it just seemed in line with the current style, but maybe I'm mistaken. The ItemHeader file is the file I have the biggest questions about both in placement of the file and the overall necessity as a separate file. Additionally, perhaps you were thinking that everything should be named Item* but I think that would lead to a lot of code duplication so I moved my initial commit against that. I'm looking forward to your feedback on this! |
Note the List header "component part" does not support different h* tags nor subheader as the SUI "component" does. SUI "component parts" often do not support nearly as many feature as their "component" counter parts. Example, a list "header" vs a "ui header". The same is true for the "ui item" vs the list "item". Though some components share the class "item" or "header", the supported features and markup are often entirely different. Because of this, I don't think we should reuse them between components. All said, I'm completely open to better proposals. |
@@ -8,6 +8,16 @@ export default class ListHeaderExample extends Component { | |||
<List.Item header='Chapter 1' description='The chapter in which we meet the characters' /> | |||
<List.Item header='Chapter 2' description='The chapter in which the bad guy is introduced' /> | |||
<List.Item header='Chapter 3' description='Spoiler alert: The chapter in which the good guy wins!' /> | |||
<List.Item> | |||
<List.ItemContent> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When there is no image/icon do we want to require ItemContent? The current ListItem file suggests no.
ListItem.js: if (media) content = <div className='content'>{content}</div>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, no content when there is no icon/image. Reference the SUI docs for the exact markup.
http://semantic-ui.com/elements/list
In the case of the Item view, adding content with no icon/image will actually break the vertically aligned variations.
In response to your previous comment, what about icon and image? Do you feel those can be reused? Also, do you think placing header into a separate ItemHeader.js file is the way to go, and if it should be its own file, do you think it should be placed under the List directory or the views/Item directory? Thanks. |
The SUI List docs appear to use the same markup as the Icon / Image components, so yes.
Yes, separate file. We have a lint rule that enforces this. It is important to keep one component per file for doc generation as well. All List subcomponents should go in the List directory. I can see the reason for asking this since the name is ItemHeader. We may end up renaming things a bit once this is all laid out. Please forgive the brevity and any typos as I am on mobile. Thanks for all the work so far! |
Do we put separate examples of using the component markup in the docs? |
Which markup are you referring to here? |
Examples of using the sub-component markup versus using regular props. |
Ah, yes please. You can reference the Accordion docs where I documented the same type of API options. I've given examples of both the sub component markup and the props for markup. I've also included docs in the props descriptions for this. |
Regarding reuse of "component parts", I did some more thorough analysis of the Header "Component Parts"Looking at every component that supports the Card Header
Dropdown Header
Item Header
List Header
Menu Header
Message Header
Modal Header
ConclusionCommon: Unique: ProposalHow about we create a // src/parts/HeaderPart.js
function HeaderPart() {
// support "header" class, href/onClick, `a` tag
} // src/collections/Menu/MenuHeader.js
import HeaderPart from 'src/parts/HeaderPart'
function MenuHeader() {
// render HeaderPart adding the "item" class when necessary
} We would then do the same for Thoughts? /cc @jamiehill would love your input here as well. |
I agree that this needs standardising. My initial thinking was that the extra functionality required by varying components should be baked into Header it's self, but on second thoughts would pollute the logic trying to cater for different edge cases. There seems to be some merit in maintaining the abstraction of using Header though. Would it not be convenient to be able to utilize the Header sub comps:
If we abstract the core header logic into a separate component part, we can indeed then still composite variations, for prescribing components, using additional header component parts. I think the key here is that we don't want to expose non-pertinent props on Header, but still use a decorated version of it in other components, while keeping the api as standardized as possible? |
I'm with you. There's just too much variation between "components" and "component parts" to standardize completely. I've moved this conversation to "RFC: Standardize Component Parts" #325. You'll find a more thorough analysis and updated proposal there. Feedback welcome. |
* The correct test for 'relaxed' would be something that | ||
* tests for useKeyOrValueAndKey which doesn't seem to be | ||
* available. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sounds like you are looking for common.propKeyOrValueToClassName
. Note there is a common prop to className test for every propUtil.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joshuazmiller it looks like this PR has gotten stale. If you add me as a collaborator to your fork, I can help update it and get it merged. LMK what your plans are. |
Are there any activity? I can start over it on the weekend. |
This one is open game IMO. It might be worth starting from scratch and just copying over the relevant files. The merge conflicts would be crazy otherwise. |
I'm opened #551, seems we can close this PR. |
Moved to #551
I don't think this is a complete solution. I see in the doc examples folder for List that we have a range of types and variations which I have yet to implement. If I could get some friendly feedback and pointers that'd be great especially since this is my first public pull request! Thank you.