This repository was archived by the owner on Mar 4, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 51
docs: improve docs for shorthand props #751
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
546bf81
refine shorthand docs
kuzhelov-ms 50a3427
update changelog
kuzhelov-ms 41fb68c
fix section of changelog entry
kuzhelov-ms 30f8253
remove unnecessary paragraph
kuzhelov-ms 5fa95d8
eliminate default-default repetition
kuzhelov-ms 306572e
'unsafely' -> 'safely'
kuzhelov-ms af3247d
improve custom tree rendering example
kuzhelov-ms 955da82
add note about falsy shorthand values
kuzhelov-ms 0d72500
make page component being functional
kuzhelov-ms 2254aa0
accept string arrays as value of code snippet
kuzhelov-ms b293d4e
refactor
kuzhelov-ms ee11420
Merge branch 'master' into docs/improve-shorthand-docs
kuzhelov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,42 +1,251 @@ | ||
| import * as React from 'react' | ||
| import { NavLink } from 'react-router-dom' | ||
| import { Header } from 'semantic-ui-react' | ||
| import DocPage from '../components/DocPage/DocPage' | ||
| import CodeSnippet from '../components/CodeSnippet' | ||
| import Button from '../../../src/components/Button/Button' | ||
|
|
||
| class ShorthandProps extends React.Component { | ||
| render() { | ||
| return ( | ||
| <DocPage title="Shorthand Props"> | ||
| <p> | ||
| Most components support two APIs: <code>shorthand</code> and <code>children</code>. With | ||
| the <code>shorthand</code> definition we are adding some props to the component in order | ||
| to define it’s content and behavior. | ||
| </p> | ||
| <p> | ||
| When defining the icon and text content displayed in the Button component, we can pass the | ||
| <code>icon</code> and <code>content</code> props. | ||
| </p> | ||
| <CodeSnippet value={'<Button icon="user" content="Click me!" />'} /> | ||
|
|
||
| <p> | ||
| With the <code>children</code> API, you are responsible for constructing the proper | ||
| markup. You are also responsible for managing the component's state, styling, and | ||
| accessibility. Due to this, you should strive to use the shorthand API wherever possible. | ||
| It is best to leave the details to the component to solve for you. | ||
| </p> | ||
| <CodeSnippet | ||
| value={[ | ||
| `<Button icon iconOnly primary>`, | ||
| ` <Icon name="book" xSpacing="none" />`, | ||
| `</Button>`, | ||
| ].join('\n')} | ||
| /> | ||
| <br /> | ||
| <Button as={NavLink} content="Quick Start" icon="arrow right" primary to="quick-start" /> | ||
| </DocPage> | ||
| ) | ||
| } | ||
| } | ||
| const header = content => <Header as="h2">{content}</Header> | ||
| const subheader = content => <Header as="h3">{content}</Header> | ||
|
|
||
| const code = content => <code>{content}</code> | ||
| const codeExample = snippet => <CodeSnippet value={snippet} /> | ||
|
|
||
| const ShorthandProps = props => ( | ||
| <DocPage title="Shorthand Props"> | ||
| <p> | ||
| It is quite common for Stardust component to have "slots" which accept shorthand values. For | ||
| example, {code('Button')} component has an {code('icon')} slot which value defines the icon | ||
| that will be rendered. | ||
| </p> | ||
|
|
||
| {codeExample(`<Button icon='chess rook' />`)} | ||
|
|
||
| <p> | ||
| There are several forms of shorthand values that can be provided, but all of them share one | ||
| common thing - each is eventually evaluated to React Element. Thus, you can think of shorthand | ||
| values as a recipe to customize rendered React Element at corresponding slot. | ||
| </p> | ||
|
|
||
| {/* SHORTHAND AS AN OBJECT */} | ||
| {header('Shorthand value as Object')} | ||
| <p> | ||
| Each component's slot has associated default element type. For example, by default there is{' '} | ||
| {code('<Icon />')} element rendered for {code('Button')}'s {code('icon')} slot. It is possible | ||
| to customize props of this default element by providing props object as shorthand value: | ||
| </p> | ||
|
|
||
| {codeExample([ | ||
| `// 'name' and 'size' will be used as <Icon /> element's props`, | ||
| `<Button icon={{ name: 'chess rook', size: 'small' }} />`, | ||
| ])} | ||
|
|
||
| {/* SHORTHAND AS A PRIMITIVE VALUE */} | ||
| {header('Shorthand value as Primitive')} | ||
| <p> | ||
| There is even shorter way to define default element's props - by using a primitive value. In | ||
| that case provided shorthand value will be taken as a value for 'default prop' of this | ||
| element. | ||
| </p> | ||
|
|
||
| <p> | ||
| This could be much easier seen with an example. Here, again, we have a {code('Button')}{' '} | ||
| element with its icon being defined with shorthand - where provided {code('string')} value | ||
| will be used as icon's {code('name')}: | ||
| </p> | ||
|
|
||
| {codeExample([ | ||
| `<>`, | ||
| ` <Button icon='chess rook' />`, | ||
| ``, | ||
| ` {/* has identical effect to the previous one */}`, | ||
| ` <Button icon={{ name: 'chess rook' }} />`, | ||
| `</>`, | ||
| ])} | ||
| <p> | ||
| This works because {code('name')} is the default prop of slot's {code('<Icon />')} element. | ||
| </p> | ||
|
|
||
| {subheader(`Disable slot's rendering`)} | ||
| <p> | ||
| It is also possible to pass falsy values ({code('false')}, {code('null')} or{' '} | ||
| {code('undefined')}) to shorthand prop - in that case there will be nothing rendered for the | ||
| component's slot. | ||
| </p> | ||
|
|
||
| {/* SHORTHAND AS REACT ELEMENT */} | ||
| {header('Shorthand value as React Element')} | ||
| <p> | ||
| There are cases where it might be necessary to customize element tree that will be rendered as | ||
| a slot's value. Returning to {code('Button')} example, we might want to render {code('<i />')}{' '} | ||
| instead of default {code('<Icon />')} element. In that case necessary element might be | ||
| directly provided as shorthand value: | ||
| </p> | ||
| {codeExample([`<Button icon={<i class='my-icon'></i>} />`])} | ||
|
|
||
| <blockquote> | ||
| <strong> | ||
| There is a very important caveat here, though: whenever React Element is directly used as a | ||
| shorthand value, it becomes client's responsibility to handle some aspects that Stardust was | ||
| originally responsible for (such as {code('styles')} or {code('accessibility')}). | ||
| </strong>{' '} | ||
| This is because, in contrast to other forms of shorthand values, Stardust-evaluated props | ||
| cannot be safely passed to the element which type is, generally, doesn't allow Stardust to | ||
| make any prior assumptions about. Due to this limitation, you should strive to use other | ||
| options for shorthand values whenever is possible - for instance, this is how previous example | ||
| can be rewritten: | ||
| </blockquote> | ||
| {codeExample([`<Button icon={{ as: 'i', className: 'my-icon' }} />`])} | ||
|
|
||
| <p> | ||
| However, there still might be cases where it would be impossible to use object form of the | ||
| shorthand value - for example, you might want to render some custom elements tree for the | ||
| slot, while wanting to preserve calculated styles and accessibility aspects for it. In that | ||
| case {code('render')} callback of function shorthand value should be used. | ||
| </p> | ||
|
|
||
| {/* SHORTHAND AS A FUNCTION */} | ||
| {header('Shorthand value as Function')} | ||
| <p> | ||
| Providing function as a shorthand value is the most involving but, at the same time, the most | ||
| powerful option for customizing component's slot. The only requirements for this function are: | ||
| </p> | ||
|
|
||
| <ul> | ||
| <li>it should finish syncronously</li> | ||
| <li>it should return React Element as a result</li> | ||
| </ul> | ||
|
|
||
| <p>Thus, in its simplest form, it could be used the following way:</p> | ||
|
|
||
| {codeExample([`<Button icon={() => <Icon name='chess rook' />} />`])} | ||
|
|
||
| <p> | ||
| However, if it will be just about that, this function form wouldn't introduce any additional | ||
| value. So, lets take a look on the scenarios where it is really helpful. | ||
| </p> | ||
|
|
||
| {subheader(`'Render' callback argument`)} | ||
| <p> | ||
| When function is provided as a shorthand value, there is a {code('render')} callback provided | ||
| as its argument. This {code('render')} argument is a function that 'knows' how to render all | ||
| the previously considered shorthand types - i.e. primitive values, objects, React Elements. | ||
| </p> | ||
|
|
||
| <p>Here is an example that represents this:</p> | ||
|
|
||
| {codeExample([ | ||
| `<>`, | ||
| ` {/* All three have the same effect: */}`, | ||
| ` <Button icon='chess rook' />`, | ||
| ``, | ||
| ` {/* render() transforms string to <Icon /> element */}`, | ||
| ` <Button icon={render => render('chess rook')} />`, | ||
| ``, | ||
| ` {/* render() transforms object to <Icon /> element */}`, | ||
| ` <Button icon={render => render({ name: 'chess rook' })} />`, | ||
| `</>`, | ||
| ])} | ||
|
|
||
| {subheader(`Handle Async data loading scenarios`)} | ||
| <p> | ||
| This 'render callback' arg makes it possible for the client to tackle scenarios where slot's | ||
| data may be fetched asynchronously. | ||
| </p> | ||
|
|
||
| <p> | ||
| Consider the following scenario: suppose we need to render {code('Button')}'s icon, but the | ||
| problem is that icon's name is fetched from some remote source. Thus, we cannot do it as | ||
| simply as that, as icon's name is not known at the moment {code('Button')} is rendered: | ||
| </p> | ||
|
|
||
| {codeExample(`<Button icon={{ name: /* unknown yet */ undefined }} />`)} | ||
|
|
||
| <p> | ||
| It is quite common case that there is some component that is responsible for data fetching | ||
| abstraction - and it is possible to 'tell' this component what should be rendered while data | ||
| is loading, as well as what should be rendered when data is fetched. | ||
| </p> | ||
| {codeExample([ | ||
| `<AsyncData`, | ||
| ` getData={fetch('https://some/url')}`, | ||
| ` onLoading={<div>Loading..</div>} // renders when data is loading`, | ||
| ` onLoaded={data => <div>Loaded Data: {data}</div>} // renders once data is fetched`, | ||
| `/>`, | ||
| ])} | ||
|
|
||
| <p> | ||
| Lets suppose that we want to render 'loading' icon before data is fetched, and switch it with | ||
| the icon which {code('name')} will be provided by fetched {code('data')} object. Also, note | ||
| that we have {code('render')} arg that knows <em>how</em> to render {code('Button')}'s icons. | ||
| </p> | ||
|
|
||
| <p>Putting it all together, we will address our async data fetch case:</p> | ||
| {codeExample([ | ||
| `<Button`, | ||
| ` icon={render => <AsyncData`, | ||
| ` getData={fetch('https://some/url')}`, | ||
| ` onLoading={<Icon name='loading' />}`, | ||
| ` /* using 'render' arg here to render icon from loaded name */`, | ||
| ` onLoaded={data => render({ name: data.iconName })}`, | ||
| ` />}`, | ||
| `/>`, | ||
| ])} | ||
|
|
||
| <p> | ||
| Note that this example can be further simplified, as {code('render')} can be used to render | ||
| 'loading' icon as well: | ||
| </p> | ||
| {codeExample([ | ||
| `<Button`, | ||
| ` icon={render => <AsyncData`, | ||
| ` getData={fetch('https://some/url')}`, | ||
| ` /* using 'render' arg here to render loading icon as well */`, | ||
| ` onLoading={render('loading')}`, | ||
| ` onLoaded={data => render({ name: data.iconName })}`, | ||
| ` />}`, | ||
| `/>`, | ||
| ])} | ||
|
|
||
| {subheader(`Customizing rendered tree`)} | ||
| <p> | ||
| There is another use case that {code('render')} callback arg is very useful for - this is the | ||
| case where custom element's tree should be rendered for the slot, but, at the same time, all | ||
| the evaluated styles and accessibility behaviors should be preserved for rendered custom | ||
| element. | ||
| </p> | ||
|
|
||
| <p> | ||
| As you might recall, there is a problem that might happen when React Element is provided | ||
| directly as shorthand value - in that case Stardust props are not propagated to rendered | ||
| element, which may result in broken styles and accessibility. In order to avoid that the | ||
| following strategy should be considered: | ||
| </p> | ||
|
|
||
| {codeExample([ | ||
| `<Button icon={render => render(`, | ||
| ` /* what to render */`, | ||
| ` { name: 'chess rook' },`, | ||
| ``, | ||
| ` /* how to render */`, | ||
| ` (ComponentType, props) => (`, | ||
| ` <div class='my-icon-container'>`, | ||
| ` <i class="my-chess-rook-icon" {...props.accessibility.root} />`, | ||
| ` </div>`, | ||
| ` )`, | ||
| `)} />`, | ||
| ])} | ||
|
|
||
| <p> | ||
| Here we are passing a recipe of how evaluated {code('ComponentType')} ({code('Icon')} in our | ||
| case, which is ignored by the rendered tree) and {code('props')} should be passed to custom | ||
| elements tree. Note that {code('props')} object, amongst others, contains all the necessary | ||
| evaluated accessibility attributes that should be applied to rendered element. | ||
| </p> | ||
|
|
||
| <br /> | ||
| <Button as={NavLink} content="Quick Start" icon="arrow right" primary to="quick-start" /> | ||
| </DocPage> | ||
| ) | ||
|
|
||
| export default ShorthandProps |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.