├── .babelrc ├── .circleci └── config.yml ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── .storybook ├── addons.js ├── config.js ├── preview-head.html └── webpack.config.js ├── LICENSE ├── README.md ├── __mocks__ └── styleMock.js ├── assets ├── icons.svg ├── icons │ ├── alarm.svg │ ├── bluetooth.svg │ ├── cached.svg │ ├── call.svg │ ├── cast.svg │ ├── check-circle.svg │ ├── chevron-right.svg │ ├── cloud-download.svg │ ├── computer.svg │ ├── email.svg │ ├── face.svg │ ├── favorite.svg │ ├── get-app.svg │ ├── headset.svg │ ├── home.svg │ ├── image.svg │ ├── mic.svg │ ├── notifications.svg │ ├── perm-identity.svg │ ├── photo-camera.svg │ ├── question-answer.svg │ ├── repeat.svg │ ├── room.svg │ ├── search.svg │ ├── settings.svg │ ├── shopping-cart.svg │ ├── thumb-up.svg │ ├── trash-can.svg │ ├── visibility.svg │ ├── volume-up.svg │ └── zoom-in.svg ├── mock │ └── images │ │ ├── 128 │ │ └── 72 │ │ │ ├── img01.jpg │ │ │ ├── img01.webp │ │ │ ├── img02.jpg │ │ │ └── img02.webp │ │ ├── 192 │ │ └── 108 │ │ │ ├── img01.jpg │ │ │ ├── img01.webp │ │ │ ├── img02.jpg │ │ │ └── img02.webp │ │ ├── 256 │ │ └── 144 │ │ │ ├── img01.jpg │ │ │ ├── img01.webp │ │ │ ├── img02.jpg │ │ │ └── img02.webp │ │ ├── 288 │ │ └── 162 │ │ │ ├── img01.jpg │ │ │ ├── img01.webp │ │ │ ├── img02.jpg │ │ │ └── img02.webp │ │ ├── 384 │ │ └── 216 │ │ │ ├── img01.jpg │ │ │ ├── img01.webp │ │ │ ├── img02.jpg │ │ │ └── img02.webp │ │ ├── 576 │ │ └── 324 │ │ │ ├── img01.jpg │ │ │ ├── img01.webp │ │ │ ├── img02.jpg │ │ │ └── img02.webp │ │ ├── 768 │ │ └── 432 │ │ │ ├── img01.jpg │ │ │ ├── img01.webp │ │ │ ├── img02.jpg │ │ │ └── img02.webp │ │ ├── 1920 │ │ └── 1080 │ │ │ ├── img01.jpg │ │ │ ├── img01.webp │ │ │ ├── img02.jpg │ │ │ └── img02.webp │ │ ├── 3840 │ │ └── 2160 │ │ │ ├── img01.jpg │ │ │ ├── img01.webp │ │ │ ├── img02.jpg │ │ │ └── img02.webp │ │ ├── img01.jpg │ │ ├── img01.webp │ │ ├── img02.jpg │ │ └── img02.webp └── static │ └── base.css ├── backstop.json ├── backstop_data └── engine_scripts │ ├── casper │ ├── clickAndHoverHelper.js │ ├── loadCookies.js │ ├── onBefore.js │ ├── onReady.js │ └── waitForHelperHelper.js │ ├── chromy │ ├── clickAndHoverHelper.js │ ├── loadCookies.js │ ├── onBefore.js │ └── onReady.js │ ├── cookies.json │ ├── onBefore.js │ └── onReady.js ├── config ├── build.js ├── env.js └── webpack.js ├── package.json ├── postcss.config.js ├── public └── index.html ├── src ├── App.jsx ├── Error.jsx ├── actions │ └── NotificationListPageActions.js ├── client.js ├── components │ ├── atoms │ │ ├── Anchor │ │ │ ├── index.js │ │ │ └── index.stories.js │ │ ├── Balloon │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── Button │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── Card │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── Heading │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ ├── index.test.js │ │ │ └── styles.css │ │ ├── HolyGrailLayout │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── HoverTipInteraction │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── Icon │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ ├── index.test.js │ │ │ └── styles.css │ │ ├── Img │ │ │ ├── index.js │ │ │ └── index.stories.js │ │ ├── MediaObjectLayout │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── StickyHeaderLayout │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── TextBox │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── Time │ │ │ ├── index.js │ │ │ └── index.stories.js │ │ └── Txt │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ ├── examples │ │ ├── LayoutThrashing │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ └── Update │ │ │ ├── index.js │ │ │ └── index.stories.js │ ├── molecules │ │ ├── Breadcrumb │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── Copyright │ │ │ ├── index.js │ │ │ └── index.stories.js │ │ ├── DeleteButton │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── MailAuthForm │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── Menu │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ └── Navigation │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ ├── organisms │ │ ├── ChannelList │ │ │ ├── index.js │ │ │ └── index.stories.js │ │ ├── Footer │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── Header │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── Notification │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ ├── NotificationList │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ └── PageHeader │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ ├── pages │ │ └── NotificationListPage.js │ ├── properties.css │ ├── storyshots.test.js │ ├── templates │ │ ├── NotificationList2Template │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ └── styles.css │ │ └── NotificationListTemplate │ │ │ ├── index.js │ │ │ ├── index.stories.js │ │ │ ├── index.test.js │ │ │ └── styles.css │ └── utils │ │ ├── HoC.js │ │ ├── a11y.js │ │ └── decorators.js ├── index.html ├── mock │ ├── ActionCreator.js │ ├── EventEmitter.js │ ├── Store.js │ ├── data.js │ └── http.js ├── reducers │ ├── NotificationListPage │ │ └── NotificationListPageReducer.js │ └── index.js ├── router │ └── Root.js ├── store │ └── index.js └── types │ └── NotificationListPageTypes.js ├── webpack.common.config.js ├── webpack.dev.config.js ├── webpack.prod.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "es2015", 5 | "react" 6 | ], 7 | "plugins": [ 8 | "add-module-exports", 9 | "add-react-displayname", 10 | "react-hot-loader/babel", 11 | "react-html-attrs", 12 | "transform-decorators-legacy", 13 | "transform-class-properties", 14 | "transform-object-rest-spread" 15 | ] 16 | } -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/node:8.5.0 6 | environment: 7 | TZ: /usr/share/zoneinfo/Asia/Tokyo 8 | steps: 9 | - checkout 10 | 11 | - restore_cache: 12 | key: v1-dependencies-{{ checksum "package.json" }} 13 | 14 | - run: yarn 15 | 16 | - save_cache: 17 | paths: 18 | - node_modules 19 | key: v1-dependencies-{{ checksum "package.json" }} 20 | 21 | - run: yarn test 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['universe', 'universe/native', 'universe/web'], 3 | }; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # testing 5 | /coverage 6 | 7 | # production 8 | /build 9 | 10 | # misc 11 | .DS_Store 12 | .env.local 13 | .env.dev 14 | .env.prod 15 | 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 150, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "jsxBracketSameLine": true, 6 | "trailingComma": "es5" 7 | } -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register'; 2 | import '@storybook/addon-chapters/register'; 3 | import '@storybook/addon-notes/register'; 4 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure, addDecorator } from '@storybook/react'; 2 | import '@storybook/addon-chapters'; 3 | import loadDirectories from 'storybook-directory-chapters'; 4 | 5 | import { storiesOf } from '@storybook/react'; 6 | 7 | const context = require.context('../src/components', true, /.stories.js$/); 8 | 9 | function loadStories() { 10 | if ( 11 | (typeof location !== 'undefined' && location.port === '9001') || 12 | process.env.STORY_SHOTS === '1' 13 | ) { 14 | loadDirectoriesFlat(context); 15 | } else { 16 | loadDirectories(context); 17 | } 18 | } 19 | 20 | configure(loadStories, module); 21 | 22 | function loadDirectoriesFlat(context) { 23 | context.keys().forEach((c) => { 24 | const dirs = getDirs(c); 25 | 26 | if (!dirs.length) return; 27 | 28 | const stories = storiesOf(dirs[dirs.length - 1], module); 29 | context(c)(stories); 30 | }); 31 | } 32 | 33 | function getDirs(path) { 34 | return path.replace(/..?\//, '').split('/').reverse().slice(1).reverse(); 35 | } 36 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 67 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | // you can use this file to add your custom webpack plugins, loaders and anything you like. 2 | // This is just the basic way to add additional webpack configurations. 3 | // For more information refer the docs: https://storybook.js.org/configurations/custom-webpack-config 4 | 5 | // IMPORTANT 6 | // When you add this file, we won't add the default configurations which is similar 7 | // to "React Create App". This only has babel loader to load JavaScript. 8 | 9 | // EXAMPLE 10 | // module.exports = { 11 | // plugins: [ 12 | // // your custom plugins 13 | // ], 14 | // module: { 15 | // loaders: [ 16 | // // add your custom loaders. 17 | // ], 18 | // }, 19 | // }; 20 | 21 | module.exports = require('../webpack.config.js'); 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kento Takano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-redux-atomic-design 2 | React & Redux & StoryBook & AtomicDesign 3 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) 4 | -------------------------------------------------------------------------------- /__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | process: () => { 3 | return {}; 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /assets/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | chevron-right 37 | Created with Sketch. 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | ico_trash 135 | Created with Sketch. 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /assets/icons/alarm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/bluetooth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/cached.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/call.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/cast.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/check-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | chevron-right 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/icons/cloud-download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/computer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/face.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/favorite.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/get-app.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/headset.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/image.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/mic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/notifications.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /assets/icons/perm-identity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/photo-camera.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/icons/question-answer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/repeat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/room.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/shopping-cart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/thumb-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/trash-can.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ico_trash 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/icons/visibility.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/volume-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/zoom-in.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/mock/images/128/72/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/128/72/img01.jpg -------------------------------------------------------------------------------- /assets/mock/images/128/72/img01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/128/72/img01.webp -------------------------------------------------------------------------------- /assets/mock/images/128/72/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/128/72/img02.jpg -------------------------------------------------------------------------------- /assets/mock/images/128/72/img02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/128/72/img02.webp -------------------------------------------------------------------------------- /assets/mock/images/192/108/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/192/108/img01.jpg -------------------------------------------------------------------------------- /assets/mock/images/192/108/img01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/192/108/img01.webp -------------------------------------------------------------------------------- /assets/mock/images/192/108/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/192/108/img02.jpg -------------------------------------------------------------------------------- /assets/mock/images/192/108/img02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/192/108/img02.webp -------------------------------------------------------------------------------- /assets/mock/images/1920/1080/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/1920/1080/img01.jpg -------------------------------------------------------------------------------- /assets/mock/images/1920/1080/img01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/1920/1080/img01.webp -------------------------------------------------------------------------------- /assets/mock/images/1920/1080/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/1920/1080/img02.jpg -------------------------------------------------------------------------------- /assets/mock/images/1920/1080/img02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/1920/1080/img02.webp -------------------------------------------------------------------------------- /assets/mock/images/256/144/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/256/144/img01.jpg -------------------------------------------------------------------------------- /assets/mock/images/256/144/img01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/256/144/img01.webp -------------------------------------------------------------------------------- /assets/mock/images/256/144/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/256/144/img02.jpg -------------------------------------------------------------------------------- /assets/mock/images/256/144/img02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/256/144/img02.webp -------------------------------------------------------------------------------- /assets/mock/images/288/162/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/288/162/img01.jpg -------------------------------------------------------------------------------- /assets/mock/images/288/162/img01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/288/162/img01.webp -------------------------------------------------------------------------------- /assets/mock/images/288/162/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/288/162/img02.jpg -------------------------------------------------------------------------------- /assets/mock/images/288/162/img02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/288/162/img02.webp -------------------------------------------------------------------------------- /assets/mock/images/384/216/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/384/216/img01.jpg -------------------------------------------------------------------------------- /assets/mock/images/384/216/img01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/384/216/img01.webp -------------------------------------------------------------------------------- /assets/mock/images/384/216/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/384/216/img02.jpg -------------------------------------------------------------------------------- /assets/mock/images/384/216/img02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/384/216/img02.webp -------------------------------------------------------------------------------- /assets/mock/images/3840/2160/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/3840/2160/img01.jpg -------------------------------------------------------------------------------- /assets/mock/images/3840/2160/img01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/3840/2160/img01.webp -------------------------------------------------------------------------------- /assets/mock/images/3840/2160/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/3840/2160/img02.jpg -------------------------------------------------------------------------------- /assets/mock/images/3840/2160/img02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/3840/2160/img02.webp -------------------------------------------------------------------------------- /assets/mock/images/576/324/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/576/324/img01.jpg -------------------------------------------------------------------------------- /assets/mock/images/576/324/img01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/576/324/img01.webp -------------------------------------------------------------------------------- /assets/mock/images/576/324/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/576/324/img02.jpg -------------------------------------------------------------------------------- /assets/mock/images/576/324/img02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/576/324/img02.webp -------------------------------------------------------------------------------- /assets/mock/images/768/432/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/768/432/img01.jpg -------------------------------------------------------------------------------- /assets/mock/images/768/432/img01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/768/432/img01.webp -------------------------------------------------------------------------------- /assets/mock/images/768/432/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/768/432/img02.jpg -------------------------------------------------------------------------------- /assets/mock/images/768/432/img02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/768/432/img02.webp -------------------------------------------------------------------------------- /assets/mock/images/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/img01.jpg -------------------------------------------------------------------------------- /assets/mock/images/img01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/img01.webp -------------------------------------------------------------------------------- /assets/mock/images/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/img02.jpg -------------------------------------------------------------------------------- /assets/mock/images/img02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kento75/react-redux-atomic-design/d04973eb40e1686f9120add7b79b224c52b0565d/assets/mock/images/img02.webp -------------------------------------------------------------------------------- /assets/static/base.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | 50 | html { 51 | font-size: 100%; 52 | } 53 | 54 | body { 55 | font-family: "Hiragino Kaku Gothic Pro", "ヒラギノ角ゴ Pro W3", Meiryo, メイリオ, sans-serif; 56 | } 57 | 58 | a { 59 | text-decoration: none; 60 | } 61 | -------------------------------------------------------------------------------- /backstop.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "gihyo_ui_component", 3 | "viewports": [ 4 | { 5 | "name": "tablet", 6 | "width": 568, 7 | "height": 1024 8 | }, 9 | { 10 | "label": "pc", 11 | "width": 1024, 12 | "height": 768 13 | } 14 | ], 15 | "onBeforeScript": "chromy/onBefore.js", 16 | "onReadyScript": "chromy/onReady.js", 17 | "scenarios": [ 18 | { 19 | "label": "Balloon 4文字ラベル", 20 | "url": "http://localhost:9001/iframe.html?selectedKind=Balloon&selectedStory=4文字ラベル", 21 | "misMatchThreshold": 0.0000000001 22 | }, 23 | { 24 | "label": "Notification", 25 | "url": "http://localhost:9001/iframe.html?selectedKind=Notification&selectedStory=デフォルト", 26 | "misMatchThreshold": 0.0000000001 27 | }, 28 | { 29 | "label": "NotificationList", 30 | "url": "http://localhost:9001/iframe.html?selectedKind=NotificationList&selectedStory=デフォルト", 31 | "misMatchThreshold": 0.0000000001 32 | }, 33 | { 34 | "label": "NotificationList2Template", 35 | "url": "http://localhost:9001/iframe.html?selectedKind=NotificationList2Template&selectedStory=デフォルト", 36 | "misMatchThreshold": 0.0000000001 37 | }, 38 | { 39 | "label": "MailAuthForm", 40 | "url": "http://localhost:9001/iframe.html?selectedKind=MailAuthForm&selectedStory=デフォルト", 41 | "misMatchThreshold": 0.0000000001 42 | } 43 | ], 44 | "paths": { 45 | "bitmaps_reference": "backstop_data/bitmaps_reference", 46 | "bitmaps_test": "backstop_data/bitmaps_test", 47 | "engine_scripts": "backstop_data/engine_scripts", 48 | "html_report": "backstop_data/html_report", 49 | "ci_report": "backstop_data/ci_report" 50 | }, 51 | "report": ["browser"], 52 | "engine": "chrome", 53 | "engineFlags": [], 54 | "asyncCaptureLimit": 5, 55 | "asyncCompareLimit": 50, 56 | "debug": false, 57 | "debugWindow": false 58 | } 59 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/casper/clickAndHoverHelper.js: -------------------------------------------------------------------------------- 1 | var WAIT_TIMEOUT = 5000; 2 | 3 | module.exports = function(casper, scenario) { 4 | var waitFor = require('./waitForHelperHelper')(casper, WAIT_TIMEOUT); 5 | var hoverSelector = scenario.hoverSelector, 6 | clickSelector = scenario.clickSelector, 7 | postInteractionWait = scenario.postInteractionWait; 8 | 9 | if (hoverSelector) { 10 | waitFor(hoverSelector); 11 | casper.then(function () { 12 | casper.mouse.move(hoverSelector); 13 | }); 14 | } 15 | 16 | if (clickSelector) { 17 | waitFor(clickSelector); 18 | casper.then(function () { 19 | casper.click(clickSelector); 20 | }); 21 | } 22 | 23 | // TODO: if postInteractionWait === integer then do ==> wait(postInteractionWait) || elsevvv 24 | waitFor(postInteractionWait); 25 | }; 26 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/casper/loadCookies.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | module.exports = function (casper, scenario) { 4 | var cookies = []; 5 | var cookiePath = scenario.cookiePath; 6 | 7 | // READ COOKIES FROM FILE IF EXISTS 8 | if (fs.exists(cookiePath)) { 9 | cookies = JSON.parse(fs.read(cookiePath)); 10 | } 11 | 12 | casper.page.cookies = cookies; 13 | console.log('Cookie state restored with cookies:', JSON.stringify(cookies, null, 2)); 14 | casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'); 15 | }; 16 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/casper/onBefore.js: -------------------------------------------------------------------------------- 1 | module.exports = function (casper, scenario, vp) { 2 | require('./loadCookies')(casper, scenario); 3 | casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'); 4 | }; 5 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/casper/onReady.js: -------------------------------------------------------------------------------- 1 | module.exports = function(casper, scenario, vp) { 2 | console.log('SCENARIO> ' + scenario.label); 3 | require('./clickAndHoverHelper')(casper, scenario); 4 | // add more helpers here... 5 | }; 6 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/casper/waitForHelperHelper.js: -------------------------------------------------------------------------------- 1 | var TIMEOUT_DEFAULT = 2000; 2 | 3 | module.exports = function (casper, timeout) { 4 | var TIMEOUT = timeout || TIMEOUT_DEFAULT; 5 | 6 | return function waitFor (selector) { 7 | if (selector) { 8 | casper.waitForSelector( 9 | selector, 10 | function () {}, 11 | function () { 12 | console.error('NOT FOUND > ' + selector); 13 | }, 14 | TIMEOUT 15 | ); 16 | } 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/chromy/clickAndHoverHelper.js: -------------------------------------------------------------------------------- 1 | module.exports = function (chromy, scenario) { 2 | var hoverSelector = scenario.hoverSelector; 3 | var clickSelector = scenario.clickSelector; 4 | var postInteractionWait = scenario.postInteractionWait; // selector [str] | ms [int] 5 | 6 | if (hoverSelector) { 7 | chromy 8 | .wait(hoverSelector) 9 | .rect(hoverSelector) 10 | .result(function (rect) { 11 | chromy.mouseMoved(rect.left, rect.top); 12 | }); 13 | } 14 | 15 | if (clickSelector) { 16 | chromy 17 | .wait(clickSelector) 18 | .click(clickSelector); 19 | } 20 | 21 | if (postInteractionWait) { 22 | chromy.wait(postInteractionWait); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/chromy/loadCookies.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | module.exports = function (chromy, scenario) { 4 | var cookies = []; 5 | var cookiePath = scenario.cookiePath; 6 | 7 | // READ COOKIES FROM FILE IF EXISTS 8 | if (fs.existsSync(cookiePath)) { 9 | cookies = JSON.parse(fs.readFileSync(cookiePath)); 10 | } 11 | 12 | // MUNGE COOKIE DOMAIN FOR CHROMY USAGE 13 | cookies = cookies.map(cookie => { 14 | cookie.url = 'https://' + cookie.domain; 15 | delete cookie.domain; 16 | return cookie; 17 | }); 18 | 19 | // SET COOKIES VIA CHROMY 20 | chromy.setCookie(cookies); 21 | console.log('Cookie state restored with:', JSON.stringify(cookies, null, 2)); 22 | }; 23 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/chromy/onBefore.js: -------------------------------------------------------------------------------- 1 | module.exports = function (chromy, scenario, vp) { 2 | require('./loadCookies')(chromy, scenario); 3 | 4 | // IGNORE ANY CERT WARNINGS 5 | chromy.ignoreCertificateErrors(); 6 | }; 7 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/chromy/onReady.js: -------------------------------------------------------------------------------- 1 | module.exports = function (chromy, scenario, vp) { 2 | console.log('SCENARIO > ' + scenario.label); 3 | require('./clickAndHoverHelper')(chromy, scenario); 4 | // add more ready handlers here... 5 | }; 6 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/cookies.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "domain": ".www.yourdomain.com", 4 | "path": "/", 5 | "name": "yourCookieName", 6 | "value": "yourCookieValue", 7 | "expirationDate": 1798790400, 8 | "hostOnly": false, 9 | "httpOnly": false, 10 | "secure": false, 11 | "session": false, 12 | "sameSite": "no_restriction" 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/onBefore.js: -------------------------------------------------------------------------------- 1 | module.exports = function (engine, scenario, vp) { 2 | // This script runs before your app loads. Edit here to log-in, load cookies or set other states required for your test. 3 | console.log('onBefore.js has run for ' + vp.label + '.'); 4 | }; 5 | -------------------------------------------------------------------------------- /backstop_data/engine_scripts/onReady.js: -------------------------------------------------------------------------------- 1 | module.exports = function (engine, scenario, vp) { 2 | engine.evaluate(function () { 3 | // Your web-app is now loaded. Edit here to simulate user interactions or other state changes in the browser window context. 4 | }); 5 | console.log('onReady.js has run for: ', vp.label); 6 | }; 7 | -------------------------------------------------------------------------------- /config/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | const projDir = path.resolve(__dirname, '..'); 6 | 7 | const src = `${ projDir }/src`; 8 | const dest = `${ projDir }/build/static`; 9 | 10 | module.exports = { 11 | 12 | production: process.env.NODE_ENV === 'production', 13 | 14 | projDir, 15 | 16 | src, 17 | 18 | js: { 19 | src: { 20 | client: [ 'babel-polyfill', `${ src }/client.js` ] 21 | }, 22 | output: { 23 | path: dest, 24 | filename: '[name].js', 25 | sourceMapFilename: '[name].js.map' 26 | }, 27 | testSrc: `${ src }/**/*.ts` 28 | }, 29 | 30 | svg: { 31 | src: `${src}/assets`, 32 | output: `${dest}/assets` 33 | } 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /config/env.js: -------------------------------------------------------------------------------- 1 | // settings for each environment 2 | -------------------------------------------------------------------------------- /config/webpack.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const webpack = require('webpack'); 4 | const path = require('path'); 5 | const ExtractTextPlugin = require("extract-text-webpack-plugin"); 6 | 7 | const build = require('./build'); 8 | 9 | module.exports = merge => { 10 | const wpc = Object.assign(merge || {}, { 11 | 12 | output: Object.assign({}, build.js.output, { 13 | libraryTarget: 'umd' 14 | }), 15 | 16 | plugins: [ 17 | new ExtractTextPlugin('app.css') 18 | ], 19 | 20 | resolve: { 21 | extensions: [ '*', '.js', '.jsx', '.css' ], 22 | modules: [ 23 | 'node_modules', 24 | `${ build.projDir }/src`, 25 | ], 26 | }, 27 | 28 | module: { 29 | 30 | rules: [{ 31 | test: /\.jsx?$/, 32 | use: 'babel-loader' 33 | }, { 34 | test: /\.css$/, 35 | use: ExtractTextPlugin.extract({ 36 | fallback: 'style-loader', 37 | use: [ 38 | { 39 | loader: 'css-loader', 40 | options: { 41 | sourceMap: true, 42 | modules: true, 43 | importLoaders: 1, 44 | localIdentName: '[local]___[hash:base64:5]', 45 | }, 46 | }, 47 | { 48 | loader: 'postcss-loader', 49 | options: { 50 | plugins: () => ([ 51 | require("postcss-import")({path: ["node_modules", build.src]}), 52 | require("postcss-cssnext")(), 53 | ]), 54 | }, 55 | }, 56 | ], 57 | }), 58 | }], 59 | 60 | }, 61 | 62 | }); 63 | 64 | if (!build.production) { 65 | wpc.devtool = '#source-map'; 66 | } 67 | 68 | return wpc; 69 | }; 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-redux-atomic-design", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "src/client.js", 6 | "author": "Kento75", 7 | "license": "MIT", 8 | "repository": {}, 9 | "scripts": { 10 | "start": "webpack-dev-server --mode development --config webpack.dev.config", 11 | "start-windows": "set NODE_ENV=development&& webpack-dev-server --mode development --config webpack.dev.config", 12 | "build": "NODE_ENV=production webpack --mode production --config webpack.prod.config.js --colors", 13 | "build-windows": "set NODE_ENV=production && webpack --config webpack.prod.config.js --mode production --colors", 14 | "build-staging": "NODE_ENV=staging webpack --mode production --config webpack.prod.config.js --colors", 15 | "lintThis": "eslint src --ext .js,.jsx --fix", 16 | "build-storybook": "build-storybook", 17 | "storybook": "start-storybook -p 6006 -s ./assets", 18 | "storybook:visual": "start-storybook -p 9001 -s ./assets", 19 | "spec:visual": "backstop reference", 20 | "test:visual": "backstop test", 21 | "approve:visual": "backstop approve", 22 | "report:visual": "backstop openReport", 23 | "test": "cross-env STORY_SHOTS=1 jest", 24 | "svg": "svg-sprite-generate -d ./assets/icons -o ./assets/icons.svg" 25 | }, 26 | "pre-commit": [ 27 | "lintThis" 28 | ], 29 | "dependencies": { 30 | "moment": "^2.22.2", 31 | "prop-types": "^15.6.2", 32 | "react": "16.4.2", 33 | "react-addons-perf": "^15.4.2", 34 | "react-dom": "16.4.2", 35 | "recompose": "^0.28.1" 36 | }, 37 | "devDependencies": { 38 | "@storybook/addon-actions": "^4.0.0-alpha.10", 39 | "@storybook/addon-chapters": "^0.6.3", 40 | "@storybook/addon-links": "^4.0.0-alpha.10", 41 | "@storybook/addon-notes": "^4.0.0-alpha.10", 42 | "@storybook/addon-storyshots": "^4.0.0-alpha.10", 43 | "@storybook/addons": "^4.0.0-alpha.10", 44 | "@storybook/channels": "^4.0.0-alpha.10", 45 | "@storybook/react": "^4.0.0-alpha.10", 46 | "autoprefixer": "^8.4.1", 47 | "axe-core": "^3.0.3", 48 | "axios": "^0.18.0", 49 | "babel-core": "^6.26.3", 50 | "babel-loader": "^7.1.5", 51 | "babel-plugin-add-module-exports": "^0.2.1", 52 | "babel-plugin-add-react-displayname": "^0.0.5", 53 | "babel-plugin-react-html-attrs": "^2.1.0", 54 | "babel-plugin-transform-class-properties": "^6.24.1", 55 | "babel-plugin-transform-decorators-legacy": "^1.3.5", 56 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 57 | "babel-polyfill": "^6.26.0", 58 | "babel-preset-env": "^1.7.0", 59 | "babel-preset-es2015": "^6.24.1", 60 | "babel-preset-react": "^6.24.1", 61 | "babel-preset-stage-0": "^6.24.1", 62 | "backstopjs": "3.1.21", 63 | "caniuse-lite": "^1.0.30000874", 64 | "clean-webpack-plugin": "^0.1.19", 65 | "cpx": "^1.5.0", 66 | "cross-env": "^5.2.0", 67 | "css-loader": "^1.0.0", 68 | "enzyme": "2.9.1", 69 | "eslint": "^4.19.1", 70 | "eslint-config-universe": "^1.0.7", 71 | "eslint-loader": "^2.1.0", 72 | "extract-text-webpack-plugin": "^4.0.0-beta.0", 73 | "favicons-webpack-plugin": "0.0.9", 74 | "html-webpack-plugin": "^3.2.0", 75 | "jest": "20.0.4", 76 | "jest-enzyme": "3.8.0", 77 | "local-web-server": "^2.6.0", 78 | "lodash": "^4.17.10", 79 | "mkdirp": "^0.5.1", 80 | "ncp": "^2.0.0", 81 | "node-sass": "^4.9.0", 82 | "optimize-css-assets-webpack-plugin": "^5.0.0", 83 | "postcss-cssnext": "^3.1.0", 84 | "postcss-flexbugs-fixes": "^3.3.1", 85 | "postcss-import": "^10.0.0", 86 | "postcss-loader": "^2.1.4", 87 | "pre-commit": "^1.2.2", 88 | "prettier": "^1.14.0", 89 | "react-dev-utils": "^5.0.1", 90 | "react-hot-loader": "^4.3.4", 91 | "react-perf-container": "^0.0.2", 92 | "react-redux": "^5.0.7", 93 | "react-router-dom": "^4.3.1", 94 | "react-test-renderer": "^15.6.1", 95 | "redux": "^4.0.0", 96 | "redux-devtools-extension": "^2.13.5", 97 | "redux-logger": "^3.0.6", 98 | "redux-promise-middleware": "^5.1.1", 99 | "redux-thunk": "^2.3.0", 100 | "sass-loader": "^7.1.0", 101 | "storybook-directory-chapters": "^0.1.1", 102 | "style-loader": "^0.21.0", 103 | "svg-sprite-generator": "^0.0.7", 104 | "uglifyjs-webpack-plugin": "^1.2.7", 105 | "url-loader": "^1.0.1", 106 | "webpack": "^4.16.4", 107 | "webpack-cli": "^3.1.0", 108 | "webpack-dev-server": "^3.1.5", 109 | "webpack-merge": "^4.1.4", 110 | "webpack-notifier": "^1.6.0" 111 | }, 112 | "jest": { 113 | "transform": { 114 | "\\.js$": "/node_modules/babel-jest", 115 | "\\.css$": "/__mocks__/styleMock.js" 116 | }, 117 | "setupTestFrameworkScriptFile": "./node_modules/jest-enzyme/lib/index.js", 118 | "collectCoverage": true, 119 | "collectCoverageFrom": [ 120 | "src/**/*.js", 121 | "!src/**/*.stories.js" 122 | ], 123 | "testPathIgnorePatterns": [ 124 | "/node_modules/" 125 | ] 126 | }, 127 | "eslintConfig": { 128 | "extends": [ 129 | "universe", 130 | "universe/native", 131 | "universe/web" 132 | ] 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-cssnext': {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React Redux Atomic Design 6 | 7 | 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import CatchError from './Error'; 3 | import Root from './router/Root'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | 15 | export default App; -------------------------------------------------------------------------------- /src/Error.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export default class CatchError extends Component { 4 | state = { 5 | hasError: false, 6 | error: null, 7 | }; 8 | componentDidCatch(error, info) { 9 | this.setState({ hasError: true, error }); 10 | console.log('hey', error, info); 11 | } 12 | 13 | render() { 14 | if (this.state.hasError) { 15 | return ( 16 |
17 |

Something went wrong.

18 |
19 | ); 20 | } 21 | return this.props.children; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/actions/NotificationListPageActions.js: -------------------------------------------------------------------------------- 1 | import request from 'axios'; 2 | import * as types from '../types/NotificationListPageTypes.js'; 3 | 4 | export function change() { 5 | // return { 6 | // type: types.CHANGE_SEARCH_WORD, 7 | // searchWord, 8 | // }; 9 | } 10 | 11 | export function deleteNotification(id) { 12 | // return { 13 | // type: types.CHANGE_SEARCH_WORD, 14 | // searchWord, 15 | // }; 16 | } 17 | 18 | /** 19 | * リクエストobject作成処理 20 | * @param {string} url APIのURL 21 | * @param {string} method REST区分(GET、POST、PUT、DELETE) 22 | * @param {object} data 送信データ 23 | */ 24 | function makeRequest(url, method, data) { 25 | return request({ 26 | url, 27 | method, 28 | data, 29 | }); 30 | } -------------------------------------------------------------------------------- /src/client.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './App'; 4 | 5 | render(( 6 | 7 | ), document.getElementById('app')); 8 | -------------------------------------------------------------------------------- /src/components/atoms/Anchor/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Anchor = ({ ...props }) => ( 4 | 5 | ); 6 | export default Anchor; 7 | -------------------------------------------------------------------------------- /src/components/atoms/Anchor/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Anchor from './index.js'; 3 | import { LinkTxt } from '../Txt/index.js'; 4 | 5 | export default stories => stories 6 | .add('デフォルト', () => ( 7 | アンカー 8 | )) 9 | .add('LinkTxtとの組み合わせ', () => ( 10 | LinkTxtとの組み合わせ 11 | )); 12 | -------------------------------------------------------------------------------- /src/components/atoms/Balloon/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import HoverTipInteraction, { Tip, Marker } from '../HoverTipInteraction/index.js'; 4 | 5 | const Balloon = ({ children, className, ...props }) => ( 6 | 7 | { children } 8 | ); 9 | 10 | export default Balloon; 11 | 12 | export const BalloonTip = ({ children, label, className, ...props }) => ( 13 | 14 | { children } 15 | { label } 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /src/components/atoms/Balloon/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { withNotes } from '@storybook/addon-notes'; 3 | import Balloon, { BalloonTip } from './index.js'; 4 | import { TrashCanIcon } from '../Icon/index.js'; 5 | import { withStyle } from '../../utils/decorators.js'; 6 | 7 | const note = withNotes('読みやすさを考慮すると 10 文字までが適当。11 文字以上を表示したい場合は別のデザインを検討すること。'); 8 | 9 | export default stories => stories 10 | .add('2文字ラベル', () => 次へ) 11 | .add('4文字ラベル', () => 削除する) 12 | .add('10文字ラベル', note(() => 削除したかったらする)) 13 | .add('20文字ラベル', note(() => 削除したかったらするけど、どうしたいかな)) 14 | .add('30文字ラベル', note(() => 削除したかったらするけど、どうしたいかな。嫌なら、やめようか)) 15 | .add('30文字ラベル改行', () => 削除したかったらするけど、どうしたいかな。
嫌なら、やめようか
) 16 | .add('絶対座標配置', () => 左上から 200px に配置) 17 | .add('アイコンラベル', () => ) 18 | .add('絵文字', () => ) 19 | .add('バルーンチップ', () => withStyle({ marginTop: '50px' })( 20 |

ここにバルーンチップを表示

21 | )) 22 | .add('長文中のバルーンチップ', () => ( 23 |

24 | 専門的なことを説明する文章の場合、文章中のある言葉が一般的に使われるものでない場合などに注釈を表示したいときがあります。たとえばバルーンチップのようなUIを使うことでそれが可能です。 25 |

26 | )) 27 | .add('BalloonTip in a long sentence', () => ( 28 |

29 | When it comes to terminology, you would like to add an note to that in order to describe the meaning. That is when BalloonTip comes to the resque. It only shows up when a user puts his or her mouse cursor on the terminology. 30 |

31 | )); 32 | -------------------------------------------------------------------------------- /src/components/atoms/Balloon/styles.css: -------------------------------------------------------------------------------- 1 | @import "../../properties.css"; 2 | 3 | .balloon { 4 | background-color: var(--color-tip); 5 | border-radius: var(--radius); 6 | color: var(--color-text-outlined); 7 | display: inline-block; 8 | font-size: var(--font-size-s); 9 | padding: 0.4rem 0.5rem; 10 | position: relative; 11 | } 12 | 13 | .balloon::after { 14 | border-color: var(--color-tip) transparent transparent transparent; 15 | border-style: solid; 16 | border-width: 3px 3px 0 3px; 17 | bottom: 0; 18 | content: ""; 19 | display: block; 20 | height: 0; 21 | left: 50%; 22 | position: absolute; 23 | transform: translate(-50%, 100%); 24 | width: 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/components/atoms/Button/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | 4 | function buttonFactory(type) { 5 | return ({ children, className, ...props }) => ( 6 | 7 | ); 8 | } 9 | 10 | export const Button = buttonFactory('default'); 11 | export const PrimaryButton = buttonFactory('primary'); 12 | export const WarningButton = buttonFactory('warning'); 13 | 14 | export default Button; 15 | -------------------------------------------------------------------------------- /src/components/atoms/Button/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button, { PrimaryButton, WarningButton } from './index.js'; 3 | 4 | export default stories => stories 5 | .add('デフォルト', () => ) 6 | .add('プライマリ', () => プライマリ) 7 | .add('警告', () => 警告); 8 | -------------------------------------------------------------------------------- /src/components/atoms/Button/styles.css: -------------------------------------------------------------------------------- 1 | @import "../../properties.css"; 2 | 3 | .button { 4 | border-radius: var(--radius); 5 | border-width: 0; 6 | display: inline-block; 7 | font-weight: var(--font-weight-bold); 8 | font-size: var(--font-size-m); 9 | line-height: 1; 10 | padding: .8rem; 11 | text-decoration: none; 12 | transition: opacity var(--hover-animation); 13 | } 14 | 15 | .button:hover { 16 | opacity: .7; 17 | } 18 | 19 | .default { 20 | background-color: inherit; 21 | border: var(--border); 22 | color: var(--color-info); 23 | } 24 | 25 | .primary { 26 | background-color: var(--color-primary); 27 | color: var(--color-text-outlined); 28 | } 29 | 30 | .warning { 31 | background-color: var(--color-warning); 32 | color: var(--color-text-outlined); 33 | } 34 | -------------------------------------------------------------------------------- /src/components/atoms/Card/index.js: -------------------------------------------------------------------------------- 1 | import React, { cloneElement } from 'react'; 2 | import styles from './styles.css'; 3 | 4 | const Card = ({ tag:Tag = 'section', className, ...props }) => ( 5 | 6 | ); 7 | export default Card; 8 | 9 | export const CardHeader = ({ className, ...props }) => ( 10 |
11 | ); 12 | -------------------------------------------------------------------------------- /src/components/atoms/Card/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Card, { CardHeader } from './index.js'; 3 | import { wrapWithStyle } from '../../utils/decorators.js'; 4 | import { HeadingOutlined } from '../../atoms/Heading/index.js'; 5 | 6 | const withBackground = wrapWithStyle({ padding: '50px', backgroundColor: 'gray', height: '100vh' }); 7 | 8 | export default stories => stories 9 | .add('デフォルト', () => withBackground( 10 | カード 11 | )) 12 | .add('見出し付き', () => withBackground( 13 | 14 | 見出し 15 | カード 16 | 17 | )); 18 | -------------------------------------------------------------------------------- /src/components/atoms/Card/styles.css: -------------------------------------------------------------------------------- 1 | @import "../../properties.css"; 2 | 3 | .card { 4 | background-color: var(--color-card); 5 | padding: calc(var(--space) * 3); 6 | } 7 | 8 | .header { 9 | background-color: var(--color-card-header); 10 | margin: calc(var(--space) * -3) calc(var(--space) * -3) calc(var(--space) * 3); 11 | padding: var(--space) calc(var(--space) * 3); 12 | } 13 | -------------------------------------------------------------------------------- /src/components/atoms/Heading/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { containPresenter } from '../../utils/HoC.js'; 4 | 5 | export const HeadingPresenter = ({ 6 | tag:Tag, 7 | visualLevel, 8 | className, 9 | ...props, 10 | }) => ( 11 | 12 | ); 13 | 14 | export const HeadingUnderlinedPresenter = ({ 15 | tag:Tag, 16 | visualLevel, 17 | className, 18 | ...props, 19 | }) => ( 20 | 21 | ); 22 | 23 | export const HeadingOutlinedPresenter = ({ 24 | tag:Tag, 25 | visualLevel, 26 | className, 27 | ...props, 28 | }) => ( 29 | 30 | ); 31 | 32 | export const HeadingContainer = ({ 33 | presenter, 34 | level = 2, 35 | visualLevel, 36 | ...props, 37 | }) => { 38 | level = Math.max(1, Math.min(6, level)); 39 | visualLevel = Math.max(1, Math.min(6, (typeof visualLevel !== 'undefined') ? visualLevel : level)); 40 | const tag = `h${ level }`; 41 | 42 | return presenter({ tag, visualLevel, ...props }); 43 | }; 44 | 45 | const Heading = containPresenter(HeadingContainer, HeadingPresenter); 46 | export default Heading; 47 | 48 | export const HeadingUnderlined = containPresenter(HeadingContainer, HeadingUnderlinedPresenter); 49 | export const HeadingOutlined = containPresenter(HeadingContainer, HeadingOutlinedPresenter); 50 | 51 | Object.assign(Heading, { displayName: 'Heading' }); 52 | -------------------------------------------------------------------------------- /src/components/atoms/Heading/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Heading, { HeadingUnderlined, HeadingOutlined } from './index.js'; 3 | import { withStyle } from '../../utils/decorators.js'; 4 | 5 | const withDarkBg = withStyle({ backgroundColor: 'black' }); 6 | 7 | export default stories => stories 8 | .add('デフォルト', () => 見出し) 9 | .add('レベル1', () => 見出しレベル1) 10 | .add('レベル1、見た目2', () => 見出しレベル1、見た目2) 11 | .add('下線付き', () => 下線付き) 12 | .add('下線付き、レベル1', () => 見出しレベル1) 13 | .add('下線付き、レベル1、見た目2', () => 下線付き、見出しレベル1、見た目2) 14 | .add('白抜き', () => withDarkBg(下線付き)) 15 | .add('白抜き、レベル1', () => withDarkBg(見出しレベル1)) 16 | .add('白抜き、レベル1、見た目2', () => withDarkBg(下線付き、見出しレベル1、見た目2)); 17 | -------------------------------------------------------------------------------- /src/components/atoms/Heading/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { HeadingContainer } from './index.js'; 3 | 4 | describe('HeadingContainer', () => { 5 | const presenter = props => props; 6 | 7 | it('見た目レベルが指定されていないとき見出しレベルに合わせる', () => { 8 | const { visualLevel } = HeadingContainer({ 9 | presenter, 10 | level: 1, 11 | }); 12 | expect(visualLevel).toBe(1); 13 | }); 14 | 15 | it('見た目レベルが指定されているときは見出しレベルに合わせない', () => { 16 | const { visualLevel } = HeadingContainer({ 17 | presenter, 18 | level: 1, 19 | visualLevel: 2, 20 | }); 21 | expect(visualLevel).toBe(2); 22 | }); 23 | 24 | it('1 未満のレベルは 1 とする', () => { 25 | const { tag, visualLevel } = HeadingContainer({ 26 | presenter, 27 | level: 0, 28 | visualLevel: 0, 29 | }); 30 | expect(tag).toBe('h1'); 31 | expect(visualLevel).toBe(1); 32 | }); 33 | 34 | it('7 以上のレベルは 6 とする', () => { 35 | const { tag, visualLevel } = HeadingContainer({ 36 | presenter, 37 | level: 7, 38 | visualLevel: 7, 39 | }); 40 | expect(tag).toBe('h6'); 41 | expect(visualLevel).toBe(6); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/components/atoms/Heading/styles.css: -------------------------------------------------------------------------------- 1 | @import "../../properties.css"; 2 | 3 | .h { 4 | font-weight: var(--font-weight-bold); 5 | line-height: 1.5; 6 | } 7 | 8 | .h1 { font-size: var(--font-size-xxxxl); } 9 | .h2 { font-size: var(--font-size-xxxl); } 10 | .h3 { font-size: var(--font-size-xxl); } 11 | .h4 { font-size: var(--font-size-xl); } 12 | .h5 { font-size: var(--font-size-l); } 13 | .h6 { font-size: var(--font-size-m); } 14 | 15 | .underlined { 16 | border-bottom: var(--border); 17 | padding-bottom: var(--space); 18 | } 19 | 20 | .outlined { 21 | color: var(--color-text-outlined); 22 | } 23 | -------------------------------------------------------------------------------- /src/components/atoms/HolyGrailLayout/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { containPresenter } from '../../utils/HoC.js'; 4 | 5 | const HolyGrailLayoutPresenter = ({ tag:Tag = 'div', parts, className, ...props }) => { 6 | const { top, bottom, main, left, right } = parts; 7 | return ( 8 | 9 | { top } 10 |
11 | { main } 12 | { left } 13 | { right } 14 |
15 | { bottom } 16 |
17 | ); 18 | }; 19 | 20 | const HolyGrailLayoutContainer = ({ presenter, children, ...props }) => { 21 | const parts = mapParts(children); 22 | return presenter({ parts, ...props }); 23 | }; 24 | 25 | const partTypes = [ 26 | 'HolyGrailTop', 27 | 'HolyGrailBottom', 28 | 'HolyGrailMain', 29 | 'HolyGrailLeft', 30 | 'HolyGrailRight', 31 | ]; 32 | 33 | function mapParts(elems) { 34 | const parts = []; 35 | elems.map(elem => { 36 | const idx = partTypes.indexOf(elem.type.displayName); 37 | if (!~idx) return; 38 | parts[idx] = elem.props.children; 39 | }); 40 | const [ top, bottom, main, left, right ] = parts; 41 | return { top, bottom, main, left, right }; 42 | } 43 | 44 | const HolyGrailLayout = containPresenter(HolyGrailLayoutContainer, HolyGrailLayoutPresenter); 45 | export default HolyGrailLayout; 46 | 47 | export const HolyGrailTop = () =>
これはレンダリングされないもの
; 48 | export const HolyGrailBottom = () =>
これはレンダリングされないもの
; 49 | export const HolyGrailMain = () =>
これはレンダリングされないもの
; 50 | export const HolyGrailLeft = () =>
これはレンダリングされないもの
; 51 | export const HolyGrailRight = () =>
これはレンダリングされないもの
; 52 | -------------------------------------------------------------------------------- /src/components/atoms/HolyGrailLayout/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import HolyGrailLayout, { 3 | HolyGrailTop, 4 | HolyGrailBottom, 5 | HolyGrailMain, 6 | HolyGrailLeft, 7 | HolyGrailRight, 8 | } from './index.js'; 9 | 10 | export default function (stories) { 11 | return stories 12 | .add( 13 | 'デフォルト', 14 | () => ( 15 | 16 | 17 |
header
18 |
19 | 20 |
footer
21 |
22 | 23 |
main
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | ) 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /src/components/atoms/HolyGrailLayout/styles.css: -------------------------------------------------------------------------------- 1 | @import "../../properties.css"; 2 | 3 | .root { 4 | display: flex; 5 | min-height: 100vh; 6 | flex-direction: column; 7 | } 8 | 9 | .body { 10 | display: flex; 11 | flex-direction: column; 12 | } 13 | 14 | .body > :nth-child(2) { 15 | order: -1; 16 | } 17 | 18 | @media (--breakpoint-s) { 19 | .body { 20 | flex: 1; 21 | flex-direction: row; 22 | } 23 | 24 | .body > :nth-child(2), 25 | .body > :last-child { 26 | flex: 0 0 12em; 27 | } 28 | 29 | .body > :first-child { 30 | flex: 1; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/atoms/HoverTipInteraction/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import styles from './styles.css'; 3 | import { containPresenter } from '../../utils/HoC.js'; 4 | 5 | const HoverTipInteractionPresenter = ({ children, className, ...props }) => ( 6 | 7 | { children } 8 | 9 | ); 10 | 11 | const HoverTipInteractionContainer = ({ presenter, children, ...props }) => { 12 | children = React.Children.map(children, child => { 13 | if (child.type.displayName === 'Tip') { 14 | const grandChild = React.Children.only(child.props.children); 15 | return React.cloneElement(grandChild, { 16 | className: [ styles.tip, grandChild.props.className ].join(' '), 17 | }); 18 | } else if (child.type.displayName === 'Marker') { 19 | const grandChild = child.props.children; 20 | return React.cloneElement(grandChild, { 21 | className: [ styles.marker, grandChild.props.className ].join(' '), 22 | }); 23 | } 24 | return child; 25 | }); 26 | return presenter({ children, ...props }); 27 | }; 28 | 29 | const HoverTipInteraction = containPresenter(HoverTipInteractionContainer, HoverTipInteractionPresenter); 30 | 31 | export default HoverTipInteraction; 32 | 33 | export const Tip = () => これはレンダリングされないもの; 34 | export const Marker = () => これはレンダリングされないもの; 35 | -------------------------------------------------------------------------------- /src/components/atoms/HoverTipInteraction/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import HoverTipInteraction, { Tip, Marker } from './index.js'; 3 | import { withStyle } from '../../utils/decorators.js'; 4 | 5 | export default stories => stories 6 | .add('デフォルト', () => withStyle({ display: 'inline-block', margin: '50px' })( 7 | 8 | ホバーしてね 9 | チップだよ 10 | 11 | )) 12 | .add('マーカー', () => withStyle({ display: 'inline-block', margin: '50px' })( 13 | 14 | ホバーしてね 15 | チップだよ 16 | 17 | )); 18 | -------------------------------------------------------------------------------- /src/components/atoms/HoverTipInteraction/styles.css: -------------------------------------------------------------------------------- 1 | @import "../../properties.css"; 2 | 3 | @keyframes fade { 4 | from { 5 | opacity: 0; 6 | } 7 | to { 8 | opacity: 1; 9 | } 10 | } 11 | 12 | @keyframes marker { 13 | to { 14 | background-color: var(--color-selected); 15 | } 16 | } 17 | 18 | .root { 19 | position: relative; 20 | } 21 | 22 | .tip { 23 | display: none; 24 | left: 50%; 25 | position: absolute; 26 | top: 0; 27 | transform: translate(-50%, -100%) translateY(-12px); 28 | white-space: nowrap; 29 | } 30 | 31 | .root:hover > .tip { 32 | display: inline-block; 33 | animation: fade var(--fade-animation); 34 | } 35 | 36 | .root:hover > .marker { 37 | background-color: var(--color-selected); 38 | animation: marker var(--fade-animation) forwards; 39 | } 40 | -------------------------------------------------------------------------------- /src/components/atoms/Icon/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { containPresenter } from '../../utils/HoC.js'; 4 | 5 | export const IconSVGList = { 6 | TrashCan: 'trash-can', 7 | ChevronRight: 'chevron-right', 8 | Alarm: 'alarm', 9 | Bluetooth: 'bluetooth', 10 | Cached: 'cached', 11 | Call: 'call', 12 | Cast: 'cast', 13 | CheckCircle: 'check-circle', 14 | CloudDownload: 'cloud-download', 15 | Computer: 'computer', 16 | Email: 'email', 17 | Face: 'face', 18 | Favorite: 'favorite', 19 | GetApp: 'get-app', 20 | Headset: 'headset', 21 | Home: 'home', 22 | Image: 'image', 23 | Mic: 'mic', 24 | Notifications: 'notifications', 25 | PermIdentity: 'perm-identity', 26 | PhotoCamera: 'photo-camera', 27 | QuestionAnswer: 'question-answer', 28 | Repeat: 'repeat', 29 | Room: 'room', 30 | Search: 'search', 31 | Settings: 'settings', 32 | ShoppingCart: 'shopping-cart', 33 | ThumbUp: 'thumb-up', 34 | Visibility: 'visibility', 35 | VolumeUp: 'volume-up', 36 | ZoomIn: 'zoom-in', 37 | }; 38 | 39 | export const IconPresenter = ({ 40 | iconName, 41 | height = 20, 42 | width = 20, 43 | ...props, 44 | }) => ( 45 | 50 | 51 | 52 | ); 53 | 54 | export const IconContainer = ({ 55 | presenter, 56 | onClick, 57 | className = '', 58 | ...props, 59 | }) => { 60 | if (onClick) className += ` ${ styles.clickable }`; 61 | return presenter({ onClick, className, ...props }); 62 | }; 63 | 64 | export const iconFactory = iconName => props => { 65 | const Icon = containPresenter(IconContainer, IconPresenter); 66 | return ; 67 | }; 68 | 69 | export const TrashCanIcon = iconFactory(IconSVGList.TrashCan); 70 | export const ChevronRightIcon = iconFactory(IconSVGList.ChevronRight); 71 | export const AlarmIcon = iconFactory(IconSVGList.Alarm); 72 | export const BluetoothIcon = iconFactory(IconSVGList.Bluetooth); 73 | export const CachedIcon = iconFactory(IconSVGList.Cached); 74 | export const CallIcon = iconFactory(IconSVGList.Call); 75 | export const CastIcon = iconFactory(IconSVGList.Cast); 76 | export const CheckCircleIcon = iconFactory(IconSVGList.CheckCircle); 77 | export const CloudDownloadIcon = iconFactory(IconSVGList.CloudDownload); 78 | export const ComputerIcon = iconFactory(IconSVGList.Computer); 79 | export const EmailIcon = iconFactory(IconSVGList.Email); 80 | export const FaceIcon = iconFactory(IconSVGList.Face); 81 | export const FavoriteIcon = iconFactory(IconSVGList.Favorite); 82 | export const GetAppIcon = iconFactory(IconSVGList.GetApp); 83 | export const HeadsetIcon = iconFactory(IconSVGList.Headset); 84 | export const HomeIcon = iconFactory(IconSVGList.Home); 85 | export const ImageIcon = iconFactory(IconSVGList.Image); 86 | export const MicIcon = iconFactory(IconSVGList.Mic); 87 | export const NotificationsIcon = iconFactory(IconSVGList.Notifications); 88 | export const PermIdentityIcon = iconFactory(IconSVGList.PermIdentity); 89 | export const PhotoCameraIcon = iconFactory(IconSVGList.PhotoCamera); 90 | export const QuestionAnswerIcon = iconFactory(IconSVGList.QuestionAnswer); 91 | export const RepeatIcon = iconFactory(IconSVGList.Repeat); 92 | export const RoomIcon = iconFactory(IconSVGList.Room); 93 | export const SearchIcon = iconFactory(IconSVGList.Search); 94 | export const SettingsIcon = iconFactory(IconSVGList.Settings); 95 | export const ShoppingCartIcon = iconFactory(IconSVGList.ShoppingCart); 96 | export const ThumbUpIcon = iconFactory(IconSVGList.ThumbUp); 97 | export const VisibilityIcon = iconFactory(IconSVGList.Visibility); 98 | export const VolumeUpIcon = iconFactory(IconSVGList.VolumeUp); 99 | export const ZoomInIcon = iconFactory(IconSVGList.ZoomIn); 100 | -------------------------------------------------------------------------------- /src/components/atoms/Icon/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { action } from '@storybook/addon-actions'; 3 | import Icon, { 4 | IconSVGList, 5 | TrashCanIcon, 6 | ChevronRightIcon, 7 | AlarmIcon, 8 | BluetoothIcon, 9 | CachedIcon, 10 | CallIcon, 11 | CastIcon, 12 | CheckCircleIcon, 13 | CloudDownloadIcon, 14 | ComputerIcon, 15 | EmailIcon, 16 | FaceIcon, 17 | FavoriteIcon, 18 | GetAppIcon, 19 | HeadsetIcon, 20 | HomeIcon, 21 | ImageIcon, 22 | MicIcon, 23 | NotificationsIcon, 24 | PermIdentityIcon, 25 | PhotoCameraIcon, 26 | QuestionAnswerIcon, 27 | RepeatIcon, 28 | RoomIcon, 29 | SearchIcon, 30 | SettingsIcon, 31 | ShoppingCartIcon, 32 | ThumbUpIcon, 33 | VisibilityIcon, 34 | VolumeUpIcon, 35 | ZoomInIcon, 36 | } from './index.js'; 37 | 38 | export default stories => stories 39 | .add('TrashCanIcon', () => ) 40 | .add('ChevronRightIcon', () => ) 41 | .add('SearchIcon', () => ) 42 | .add('SettingsIcon', () => ) 43 | .add('クリッカブル', () => ) 44 | .add('一覧', () => ( 45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
78 | )); 79 | -------------------------------------------------------------------------------- /src/components/atoms/Icon/index.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import { TrashCanIcon } from './index.js'; 4 | 5 | describe('TrashCanIcon', () => { 6 | it('クリックをコールバックする', () => { 7 | const onClick = jest.fn(); 8 | const wrapper = shallow(); 9 | wrapper.simulate('click'); 10 | expect(onClick.mock.calls.length).toBe(1); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/components/atoms/Icon/styles.css: -------------------------------------------------------------------------------- 1 | .clickable { 2 | cursor: pointer; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/atoms/Img/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { containPresenter } from '../../utils/HoC.js'; 3 | 4 | const ImgPresenter = ({ src, srcSet, webpSrcSet, alt, width, height, ...props }) => ( 5 | 6 | 7 | { 12 | 13 | ); 14 | 15 | const riasRegexp = /images\/([0-9]+)\/([0-9]+)/; 16 | 17 | function createSrc(src, width, height) { 18 | if (!width || !height) return src; 19 | 20 | const ratio = window.devicePixelRatio || 1; 21 | const w = width * ratio; 22 | const h = height * ratio; 23 | return src.replace(riasRegexp, (match, p1, p2) => `images/${ w }/${ h }`); 24 | } 25 | 26 | function createSrcSet(src, width, height, extension) { 27 | if (extension) { 28 | src = src.replace(/\.[a-z0-9]+[^#\?]?/, `.${ extension }`); 29 | } 30 | if ( 31 | !riasRegexp.test(src) || 32 | !width || 33 | !height 34 | ) return src; 35 | 36 | const [ path, rest ] = src.split('images/'); 37 | const file = rest.match(".+/(.+?)([\?#;].*)?$")[1]; 38 | 39 | return [ 1, 1.5, 2, 3, 4 ] 40 | .map(dpr => `${ path }images/${ width * dpr }/${ height * dpr }/${ file } ${ dpr }x`) 41 | .join(', '); 42 | } 43 | 44 | const ImgContainer = ({ presenter, src, width, height, ...props }) => { 45 | const srcSet = createSrcSet(src, width, height); 46 | const webpSrcSet = createSrcSet(src, width, height, 'webp'); 47 | src = createSrc(src, width, height); 48 | return presenter({ src, srcSet, webpSrcSet, width, height, ...props }); 49 | }; 50 | 51 | const Img = containPresenter(ImgContainer, ImgPresenter); 52 | export default Img; 53 | 54 | Object.assign(Img, { displayName: 'Img' }); 55 | -------------------------------------------------------------------------------- /src/components/atoms/Img/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Img from './index.js'; 3 | 4 | export default stories => stories 5 | .add('デフォルト', () => ) 7 | .add('適切なサイズ指定', () => ) 8 | .add('20倍の画像', () => ); 9 | -------------------------------------------------------------------------------- /src/components/atoms/MediaObjectLayout/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | 4 | const MediaObjectLayout = ({ children, className, tag:Tag = 'div' }) => ( 5 | 6 |
{ children[0] }
7 |
{ children.slice(1) }
8 |
9 | ); 10 | 11 | export default MediaObjectLayout; 12 | -------------------------------------------------------------------------------- /src/components/atoms/MediaObjectLayout/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MediaObjectLayout from './index.js'; 3 | 4 | export default stories => stories 5 | .add('デフォルト', () => ( 6 | 7 |
8 | MediaObjectLayout を説明するためのサンプル画像 9 |
10 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ut dictum purus. Praesent id pulvinar sem, eu congue velit. Etiam porta luctus tellus, quis finibus quam condimentum eu. Cras vestibulum mauris non tempus congue.

11 |

Sed pellentesque suscipit ex sed consequat. Fusce lobortis tincidunt euismod. Etiam sollicitudin molestie semper. Donec mi sem, molestie at molestie id, posuere ac lectus. Duis mollis, mauris venenatis sagittis porta, quam velit dictum diam, non aliquam nunc elit ut ex.

12 |
13 | )) 14 | .add('section 指定', () => ( 15 | 16 |
17 | MediaObjectLayout を説明するためのサンプル画像 18 |
19 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ut dictum purus. Praesent id pulvinar sem, eu congue velit. Etiam porta luctus tellus, quis finibus quam condimentum eu. Cras vestibulum mauris non tempus congue.

20 |

Sed pellentesque suscipit ex sed consequat. Fusce lobortis tincidunt euismod. Etiam sollicitudin molestie semper. Donec mi sem, molestie at molestie id, posuere ac lectus. Duis mollis, mauris venenatis sagittis porta, quam velit dictum diam, non aliquam nunc elit ut ex.

21 |
22 | )); 23 | -------------------------------------------------------------------------------- /src/components/atoms/MediaObjectLayout/styles.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | } 4 | 5 | .body { 6 | flex: 1; 7 | min-width: 0; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/atoms/StickyHeaderLayout/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | 4 | const StickyHeaderLayout = ({ tag:Tag = 'div', children, ...props }) => ( 5 | 6 |
{ children[0] }
7 |
{ children[1] }
8 |
9 | ); 10 | 11 | export default StickyHeaderLayout; 12 | -------------------------------------------------------------------------------- /src/components/atoms/StickyHeaderLayout/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import StickyHeaderLayout from './index.js'; 3 | 4 | export default stories => stories 5 | .add('デフォルト', () => ( 6 | 7 |
ヘッダー
8 |

本体

9 |
10 | )); 11 | -------------------------------------------------------------------------------- /src/components/atoms/StickyHeaderLayout/styles.css: -------------------------------------------------------------------------------- 1 | @import "../../properties.css"; 2 | 3 | .sticky { 4 | position: fixed; 5 | width: 100%; 6 | z-index: var(--z-header); 7 | } 8 | 9 | .body { 10 | min-height: 100vh; 11 | } 12 | -------------------------------------------------------------------------------- /src/components/atoms/TextBox/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | 4 | const TextBox = ({ className, ...props }) => ( 5 | 6 | ); 7 | 8 | export default TextBox; 9 | -------------------------------------------------------------------------------- /src/components/atoms/TextBox/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TextBox from './index.js'; 3 | 4 | export default stories => stories 5 | .add('デフォルト', () => ); 6 | -------------------------------------------------------------------------------- /src/components/atoms/TextBox/styles.css: -------------------------------------------------------------------------------- 1 | @import "../../properties.css"; 2 | 3 | .textbox { 4 | border: var(--border); 5 | border-radius: var(--radius); 6 | color: var(--color-text); 7 | font-size: var(--font-size-s); 8 | padding: var(--space); 9 | } 10 | -------------------------------------------------------------------------------- /src/components/atoms/Time/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import moment from 'moment'; 3 | import 'moment/locale/ja'; 4 | import { containPresenter } from '../../utils/HoC.js'; 5 | 6 | export const TimePresenter = props =>
19 | 20 | ); 21 | export default Menu; 22 | -------------------------------------------------------------------------------- /src/components/molecules/Menu/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Menu from './index.js'; 3 | import { channels } from '../../../mock/data.js'; 4 | 5 | export default stories => stories 6 | .add('デフォルト', () => { channels }); 7 | -------------------------------------------------------------------------------- /src/components/molecules/Menu/styles.css: -------------------------------------------------------------------------------- 1 | @import "../../properties.css"; 2 | 3 | .list { 4 | margin-top: calc(var(--space) * -1); 5 | margin-left: calc(var(--space) * -3); 6 | margin-right: calc(var(--space) * -3); 7 | } 8 | 9 | .link { 10 | display: block; 11 | padding: var(--space) calc(var(--space) * 3); 12 | font-weight: 700; 13 | } 14 | 15 | .link:hover { 16 | background-color: var(--color-info-layer1); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/molecules/Navigation/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import { withStyle } from '../../utils/decorators.js'; 4 | 5 | const Navigation = ({ items, ...props }) => ( 6 | 19 | ); 20 | export default Navigation; 21 | -------------------------------------------------------------------------------- /src/components/molecules/Navigation/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Navigation from './index.js'; 3 | import { withStyle } from '../../utils/decorators.js'; 4 | 5 | const items = [ 6 | { label: 'ホーム', url: '#' }, 7 | { label: '番組表', url: '#' }, 8 | { label: '通知番組', url: '#', current: true }, 9 | { label: 'お知らせ', url: '#' }, 10 | { label: '設定', url: '#' }, 11 | ]; 12 | 13 | export default stories => stories 14 | .add('デフォルト', () => withStyle({ backgroundColor: 'black', padding: '1rem' })( 15 | 16 | )); 17 | -------------------------------------------------------------------------------- /src/components/molecules/Navigation/styles.css: -------------------------------------------------------------------------------- 1 | @import "../../properties.css"; 2 | 3 | .list { 4 | display: flex; 5 | flex-direction: rows; 6 | font-size: .8rem; 7 | } 8 | 9 | .item + .item { 10 | padding: 0 0 0 2em; 11 | } 12 | 13 | .link { 14 | color: #fff; 15 | padding: calc(var(--space) * 0.5) 0; 16 | } 17 | 18 | .link:hover, .current { 19 | border-color: var(--color-primary); 20 | border-style: solid; 21 | border-width: 0 0 .1rem 0; 22 | color: #fff; 23 | } 24 | -------------------------------------------------------------------------------- /src/components/organisms/ChannelList/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Menu from '../../molecules/Menu/index.js'; 3 | 4 | const ChannelList = ({ heading="チャンネル一覧", channels, ...props }) => ( 5 | { channels } 6 | ); 7 | export default ChannelList; 8 | -------------------------------------------------------------------------------- /src/components/organisms/ChannelList/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ChannelList from './index.js'; 3 | import { channels } from '../../../mock/data.js'; 4 | 5 | export default stories => stories 6 | .add('デフォルト', () => ); 7 | -------------------------------------------------------------------------------- /src/components/organisms/Footer/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.css'; 3 | import Copyright from '../../molecules/Copyright/index.js'; 4 | 5 | const Footer = ({ tag:Tag = 'footer', className, ...props }) => ( 6 | 7 | Yusuke Goto 8 | 9 | ); 10 | export default Footer; 11 | -------------------------------------------------------------------------------- /src/components/organisms/Footer/index.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Footer from './index.js'; 3 | 4 | export default stories => stories 5 | .add('デフォルト', () =>