1 | # React HK Components
|
2 |
|
3 | Reusable React components. A sister library of [ember-hk-components](https://github.com/heroku/ember-hk-components).
|
4 |
|
5 |
|
6 | ## Assumptions
|
7 |
|
8 | Usage of these components assumes you are using the [Purple3 CSS framework](https://purple3.herokuapp.com/) and [Malibu](https://hk-malibu.herokuapp.com).
|
9 |
|
10 |
|
11 | ## Usage
|
12 |
|
13 | ### Installation
|
14 |
|
15 | `yarn add @heroku/react-hk-components`
|
16 |
|
17 | If you want to use `HKFlagIcon`, you will need to tell Webpack to copy the flag images into a directory that is served up by your app:
|
18 |
|
19 | ```js
|
20 | // somewhere in your webpack config...
|
21 | plugins: [
|
22 | new CopyWebpackPlugin([
|
23 | {
|
24 | from: 'node_modules/@heroku/react-hk-components/dist/flags',
|
25 | to: 'flags', // this is relative to your the outputPath you configured in webpack
|
26 | }
|
27 | ]),
|
28 | ],
|
29 | ```
|
30 |
|
31 | `<HKFlagIcon>` defaults to loading the flag images from `/static/dist/flags`, but if you have a different base path to the flag images, you'll need to pass it as a prop like `<HKFlagIcon basePath='/foo/bar/flags' region='europe' />`.
|
32 |
|
33 |
|
34 | ### Components
|
35 |
|
36 | See [react-hk-components.herokuapp.com](https://react-hk-components.herokuapp.com)
|
37 | for a complete list of components that are available.
|
38 |
|
39 | #### HKModal
|
40 |
|
41 | The HKModal component displays modal dialogs of two kinds: normal, which appear in the middle of the browser viewport, and flyout, which slides in from the right. It takes the following props:
|
42 |
|
43 | * **`isFlyout`**: `boolean?`. Defaults to false.
|
44 | * **`show`**: `boolean`. Set it to `true` in order to trigger display of the modal, or `false` to trigger hiding.
|
45 | * **`type`**: `string?`. Set to `destructive` if you want the title of the modal to be rendered in red.
|
46 | * **`onDismiss`**: `(value?: string) => any`. A handler that is invoked with the close value when the modal is dismissed. Closing the modal by clicking outside the modal or by clicking on the X at the top-right of the modal will result in the handler being invoked with a value of `cancel`.
|
47 | * **`header`**: JSX element. What is rendered in the header of the modal.
|
48 | * **`buttons`**: `IButtonDefinition[]?`: an optional array of button definitions. These will be rendered left-to-right as buttons in the footer of the modal.
|
49 |
|
50 | The contents of the modal are the children passed in the body of the react element, e.g. `<HKModal> stuff to render in body of modal </HKModal>`
|
51 |
|
52 | Buttons are defined as follows:
|
53 |
|
54 | * **`text`**: `string`. The text on the button
|
55 | * **`type`**: `Button.Type`. Primary, secondary, danger, etc.
|
56 | * **`disabled`**: `boolean`. Set to `true` if you want the button disabled.
|
57 | * **`value`**: `string`. The value associated with the button. This is what will be remitted to the `onDismiss` handler when the user closes the modal by clicking on this button.
|
58 |
|
59 | So here's a working example:
|
60 |
|
61 | ```tsx
|
62 | public class MyModalWrapper extends React.Component {
|
63 | handleDismiss = (value?: string): any => {
|
64 | switch(value) {
|
65 | case 'ok':
|
66 | // handle the OK case
|
67 | break
|
68 | case cancel:
|
69 | default:
|
70 | // handle the cancel case, which is also the default
|
71 | }
|
72 | }
|
73 | public render() {
|
74 | return (<HKModal
|
75 | isFlyout={false}
|
76 | show={true}
|
77 | onDismiss={this.handleDismiss}
|
78 | header={<div>My Modal</div>}
|
79 | buttons={[
|
80 | {text: 'cancel', value: 'cancel', type: 'tertiary'},
|
81 | {text: 'OK', value: 'ok', type: 'primary'},
|
82 | ]}
|
83 | >
|
84 | <div>Look at my shiny modal content!</div>
|
85 | <p>Such shiny. Much wow.</p>
|
86 | </HKModal>)
|
87 | }
|
88 | }
|
89 |
|
90 |
|
91 |
|
92 | ```
|
93 |
|
94 | #### HKDropdown
|
95 |
|
96 | The HKDropdown component consists of a HKButton that toggles the display of it's dropdown contents. It takes the following props:
|
97 |
|
98 | * **`align`**: `Align?`. Aligns dropdown menu anchoring left or right side of dropdown button. Options are `left` or `right`. Defaults to `left`.
|
99 | * **`className`**: `string?`. Styling for the dropdown button.
|
100 | * **`closeOnClick`**: `boolean?`. Specifies whether the dropdown menu should toggle to close after clicking inside the dropdown menu. Defaults to `true`
|
101 | * **`contentClassName`**: `string?`. Styling for the dropdown menu.
|
102 | * **`disabled`**: `boolean?`: disables the button from toggling the dropdown menu. Defaults to `false`
|
103 | * **`name`**: `string?`: name of the dropdown, used for testing for `data-testid`. Defaults to `hkdropdown`.
|
104 | * **`title`**: `string?`: The title of the dropdown button.
|
105 |
|
106 | The contents of the dropdown menu are the children passed in the body of the react element, e.g. `<HKDropdown> stuff to render inside dropdown menu </HKDropdown>`
|
107 |
|
108 | Best practices for content in dropdown menu:
|
109 |
|
110 | * Each dropdown menu item should have the class `hk-dropdown-item`
|
111 | * Thematic breaks between elements (i.e. separators) should be used with `<li className='hk-dropdown-divider' />`
|
112 | * Dangerous menu items should have the class `hk-dropdown-item--danger`
|
113 |
|
114 | Refer to the stories for examples.
|
115 |
|
116 | ## Development
|
117 |
|
118 | ### Installation
|
119 |
|
120 | * `git clone https://github.com/heroku/react-hk-components`
|
121 | * `cd react-hk-components`
|
122 | * `yarn`
|
123 |
|
124 | ### Running
|
125 |
|
126 | * `yarn storybook`
|
127 | * Visit your app at [http://localhost:9001](http://localhost:9001).
|
128 |
|
129 | ### Local Usage in Another Application
|
130 |
|
131 | The demo app is useful for developing this addon, but it can often be
|
132 | helpful to consume your version of this addon in another application
|
133 | either to more easily develop your changes or to validate that your
|
134 | changes work as you expect. You can use your local version of
|
135 | `react-hk-components` in another application that consumes it via
|
136 | yarn's [link](https://yarnpkg.com/lang/en/docs/cli/link/) command.
|
137 |
|
138 | ```console
|
139 | # in your react-hk-components directory
|
140 | $ yarn link
|
141 |
|
142 | # in your consuming app directory
|
143 | $ yarn link @heroku/react-hk-components
|
144 |
|
145 | # You will need to re-run yarn in your consuming app directory and restart your app
|
146 | $ yarn
|
147 | ```
|
148 |
|
149 | You can check the success of linking in your consuming app
|
150 | `ls -l node_modules/@heroku/react-hk-components`
|
151 | which should return a symlink to your development copy of rhkc
|
152 |
|
153 | Now, when you make changes in your copy of `react-hk-components` those
|
154 | changes will be reflected in the consuming application.
|
155 |
|
156 | Caveats when linking - you will need to rebuild each time you want to see
|
157 | changes in your consuming app directory.
|
158 | `yarn build`
|
159 |
|
160 | To simplify, you can install [watch](https://www.npmjs.com/package/watch#cli) or a similar tool and run `watch 'yarn run build' src` in `react-hk-components`.
|
161 | This will rebuild the bundled version that your consuming app directory
|
162 | pulls in whenever you make changes in rhkc.
|
163 |
|
164 | Remember to unlink rhkc once finished.
|
165 |
|
166 | ### Demo app
|
167 |
|
168 | This repo can be deployed to Heroku as a demo app using
|
169 | [Storybook](https://storybook.js.org/).
|
170 |
|
171 | ```sh
|
172 | heroku create
|
173 | # ensure that heroku installs the dev dependency storybook
|
174 | heroku config:set NPM_CONFIG_PRODUCTION=false
|
175 | git push heroku master
|
176 | ```
|
177 |
|
178 | ### Releasing
|
179 |
|
180 | Unfortunately, releasing is a bit of a mess right now because the different
|
181 | pieces don't play together nicely. Specifically, [`yarn publish` doesn't support
|
182 | NPM's 2FA yet (yarn/#4904)](https://github.com/yarnpkg/yarn/issues/4904).
|
183 |
|
184 | #### Setup (one time)
|
185 |
|
186 | 1. Make sure you have a late-model npm installed that supports 2FA:
|
187 | `npm install -g npm`
|
188 | 2. Install `np` globally using npm: `npm install -g np`
|
189 |
|
190 | #### Release
|
191 |
|
192 | Because `yarn publish` doesn't support 2FA, we will have to invoke `np` with the
|
193 | `--no-yarn` option. This is a bit unfortunate because `np` is actually doing its
|
194 | sanity checks using `npm`, which might result in different behavior compared to
|
195 | `yarn`. Because of this, we should manually run the test suite ourselves ahead
|
196 | of `np` until such time as yarn gets unbroken.
|
197 |
|
198 | 1. `rm -rf node_modules`
|
199 | 2. `yarn`
|
200 | 3. `yarn test`. No errors? Lovely, proceed.
|
201 | 4. When you're ready to publish, `np --no-yarn`.
|