mirror of https://github.com/openedx/paragon.git
This is a BREAKING CHANGE. * update storybook, webpack, and other supporting packages * upgrade node version in travis * fix: clean up commonly repeated storybook configuration * fix: update dependencies * fix: misc config changes * fix: height of fieldset long term the .form-control class should not be used on fieldset. It now explicitly sets a height and was probably not intended to be used the way it is in paragon's fieldset. * fix: remove css module import for font awesome in Icon story * fix: update storyshot config to have no decorators * fix: use whitelist for npm publishing * fix: remove source maps from build. hopefully using less memory * fix: update notifications to paragon@edx.org feat: remove static build output (#398) Removes a the static build output. Sets the stage to remove css modules and simplify JSX elsewhere. feat: remove css module and namespace support (#399) This changes the way scss should be imported with components. Rather than a SCSS file for each module, this prefers that consumers import the scss file (src/index.scss). It must be included after bootstrap variables are set. It's also up to the consumer to include font-awesome if needed. See ADR for removal of CSS Modules. * fix: remove usage of css module styles object * feat: remove css modules and consolidate scss inclusion pattern * fix: build scss separately * fix: remove array around className prop for raw html nodes
This commit is contained in:
parent
8af1f5d6b4
commit
0274806f8e
22
.babelrc
22
.babelrc
|
|
@ -1,13 +1,21 @@
|
|||
{
|
||||
"presets": [
|
||||
["env", {
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "ie 11"]
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "ie 11"]
|
||||
}
|
||||
}
|
||||
}],
|
||||
"babel-preset-react"
|
||||
],
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
"transform-object-rest-spread"
|
||||
]
|
||||
"@babel/plugin-proposal-object-rest-spread"
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": ["require-context-hook"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
coverage/*
|
||||
node_modules
|
||||
static
|
||||
themeable
|
||||
dist/
|
||||
node_modules/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"extends": "eslint-config-edx",
|
||||
"parser": "babel-eslint",
|
||||
"rules": {
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{
|
||||
"devDependencies": [
|
||||
"**/*.stories.jsx",
|
||||
"src/setupTest.js",
|
||||
"**/*.test.jsx",
|
||||
"**/*.test.js",
|
||||
"config/*.js",
|
||||
"*.config.js",
|
||||
"*.config.*.js",
|
||||
"**/*.config.js"
|
||||
]
|
||||
}
|
||||
],
|
||||
// https://github.com/evcohen/eslint-plugin-jsx-a11y/issues/340#issuecomment-338424908
|
||||
"jsx-a11y/anchor-is-valid": [ "error", {
|
||||
"components": [ "Link" ],
|
||||
"specialLink": [ "to" ]
|
||||
}]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.stories.jsx"],
|
||||
"rules": {
|
||||
"no-console": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
"env": {
|
||||
"jest": true
|
||||
},
|
||||
"globals": {
|
||||
"newrelic": false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"extends": ["eslint-config-edx"],
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"react/no-array-index-key": "off",
|
||||
"compat/compat": "error",
|
||||
"jsx-a11y/anchor-is-valid": [ "error", {
|
||||
"components": [ "a" ],
|
||||
"specialLink": [ "hrefLeft", "hrefRight" ],
|
||||
"aspects": [ "noHref", "invalidHref", "preferButton" ]
|
||||
}]
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"node": {
|
||||
"extensions": [".js", ".jsx"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"jest": true,
|
||||
"browser": true
|
||||
},
|
||||
"plugins": ["compat"],
|
||||
"overrides": {
|
||||
"files": ["*.stories.jsx", "*.test.jsx"],
|
||||
"rules": {
|
||||
"import/no-extraneous-dependencies": "off" // storybook & enzyme should stay devDependencies
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,5 +4,4 @@ node_modules
|
|||
npm-debug.log
|
||||
coverage
|
||||
jest*
|
||||
static
|
||||
themeable
|
||||
dist
|
||||
|
|
|
|||
13
.npmignore
13
.npmignore
|
|
@ -1,13 +0,0 @@
|
|||
.DS_Store
|
||||
.eslintcache
|
||||
.storybook
|
||||
node_modules
|
||||
npm-debug.log
|
||||
.travis.yml
|
||||
.babelrc
|
||||
.eslintignore
|
||||
.eslintrc.json
|
||||
commitlint.config.js
|
||||
webpack.config.js
|
||||
**.test.js
|
||||
**.stories.js
|
||||
|
|
@ -2,18 +2,10 @@
|
|||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
|
||||
const styles = {
|
||||
main: {
|
||||
margin: 15,
|
||||
maxWidth: 600,
|
||||
lineHeight: 1.4,
|
||||
fontFamily: '"Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif',
|
||||
},
|
||||
};
|
||||
|
||||
storiesOf('Paragon', module)
|
||||
.add('Welcome', () => (
|
||||
<div style={styles.main}>
|
||||
<div>
|
||||
<h1>💎 Paragon</h1>
|
||||
<p>
|
||||
This is a documentation and demo space for the Paragon accessible UI component
|
||||
|
|
|
|||
|
|
@ -1,17 +1,7 @@
|
|||
import initStoryshots from '@storybook/addon-storyshots';
|
||||
import '../src/utils/reactResponsive.mock';
|
||||
|
||||
// per https://github.com/storybooks/storybook/issues/2522
|
||||
// TypeError: (0 , _reactDom.findDOMNode) is not a function errors due to a11y addon
|
||||
jest.mock("react-dom", () => {
|
||||
return {
|
||||
render: () => null,
|
||||
unmountComponentAtNode: () => null,
|
||||
findDOMNode: () => null,
|
||||
createPortal: () => null,
|
||||
};
|
||||
});
|
||||
|
||||
initStoryshots({
|
||||
initStoryshots({
|
||||
storyKindRegex: /^((?!.*?Modal).)*$/,
|
||||
configPath: '.storybook/storyshots.config.js',
|
||||
});
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +1,2 @@
|
|||
import '@storybook/addon-options/register';
|
||||
import '@storybook/addon-actions/register';
|
||||
import '@storybook/addon-a11y/register';
|
||||
import 'storybook-readme/register';
|
||||
|
|
|
|||
|
|
@ -1,31 +1,34 @@
|
|||
import React from 'react';
|
||||
import { configure, addDecorator } from '@storybook/react';
|
||||
import { setOptions } from '@storybook/addon-options';
|
||||
import { setDefaults } from '@storybook/addon-info';
|
||||
import { addParameters, addDecorator, configure } from '@storybook/react';
|
||||
import { setConsoleOptions } from '@storybook/addon-console';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
|
||||
import CssJail from '../src/CssJail';
|
||||
// Style applied to all stories
|
||||
import "./style.scss";
|
||||
|
||||
setDefaults({
|
||||
inline: false,
|
||||
header: true,
|
||||
source: true,
|
||||
setConsoleOptions({
|
||||
panelExclude: ['warn', 'error'],
|
||||
});
|
||||
|
||||
setTimeout(() => setOptions({
|
||||
name: '💎 PARAGON',
|
||||
url: 'https://github.com/edx/paragon',
|
||||
showDownPanel: true,
|
||||
downPanelInRight: true,
|
||||
}), 1000);
|
||||
// Option defaults:
|
||||
addParameters({
|
||||
options: {
|
||||
brandTitle: '💎 PARAGON',
|
||||
brandUrl: 'https://github.com/edx/paragon',
|
||||
},
|
||||
info: {
|
||||
inline: false,
|
||||
source: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
addDecorator(withInfo);
|
||||
addDecorator(storyFn => <div className="p-5">{storyFn()}</div>);
|
||||
|
||||
|
||||
const req = require.context('../src', true, /\.stories\.jsx$/);
|
||||
|
||||
addDecorator(story => (
|
||||
<CssJail>
|
||||
{story()}
|
||||
</CssJail>
|
||||
));
|
||||
|
||||
function loadStories() {
|
||||
require('./Paragon.stories.jsx');
|
||||
req.keys().forEach((filename) => req(filename));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
import React from 'react';
|
||||
import { configure } from '@storybook/react';
|
||||
|
||||
const req = require.context('../src', true, /\.stories\.jsx$/);
|
||||
|
||||
function loadStories() {
|
||||
req.keys().forEach((filename) => req(filename));
|
||||
}
|
||||
|
||||
configure(loadStories, module);
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@import "~font-awesome/css/font-awesome.min.css";
|
||||
@import '~@edx/edx-bootstrap/sass/open-edx/theme';
|
||||
@import "~bootstrap/scss/bootstrap.scss";
|
||||
@import "../src/index.scss";
|
||||
|
|
@ -1,43 +1,33 @@
|
|||
const path = require('path');
|
||||
const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
|
||||
|
||||
// This config is merged with Storybook's default
|
||||
// https://storybook.js.org/docs/configurations/custom-webpack-config/#extend-mode
|
||||
module.exports = {
|
||||
plugins: [
|
||||
new WebpackBuildNotifierPlugin({
|
||||
title: 'Paragon Storybook Build',
|
||||
warningSound: true,
|
||||
failureSound: true,
|
||||
}),
|
||||
],
|
||||
devtool: "source-map",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.scss|\.css$/,
|
||||
use: [
|
||||
loaders: [
|
||||
{
|
||||
loader: 'style-loader',
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
modules: true,
|
||||
localIdentName: '[local]',
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
data: '@import "paragon-reset";',
|
||||
includePaths: [
|
||||
path.join(__dirname, '../src/utils'),
|
||||
path.join(__dirname, '../node_modules'),
|
||||
],
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
include: path.resolve(__dirname, '../'),
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|ttf|svg|eot)(\?v=\d+\.\d+\.\d+)?$/,
|
||||
|
|
@ -45,4 +35,4 @@ module.exports = {
|
|||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
@ -1,15 +1,14 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 10
|
||||
- 8
|
||||
- 6
|
||||
cache:
|
||||
directories:
|
||||
- "~/.npm"
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- arizzitano@edx.org
|
||||
- jbradley@edx.org
|
||||
- paragon@edx.org
|
||||
on_success: never
|
||||
on_failure: always
|
||||
hipchat:
|
||||
|
|
|
|||
25
README.md
25
README.md
|
|
@ -21,17 +21,30 @@ Paragon requires React 16 or higher. To install Paragon into your project:
|
|||
npm i --save @edx/paragon
|
||||
```
|
||||
|
||||
Since Paragon is a React library, components must be rendered with React and ReactDOM. This doesn't mean the entire page has to be rendered with React. You can read up about rendering React into a page within the [official ReactDOM documentation](https://reactjs.org/docs/react-dom.html).
|
||||
In your JSX files:
|
||||
```
|
||||
import { ComponentName } from '@edx/paragon';
|
||||
```
|
||||
|
||||
### Export Targets
|
||||
And in your SCSS file:
|
||||
```
|
||||
import '~bootstrap/scss/bootstrap'; // Or other inclusion of bootstrap
|
||||
import '~@edx/paragon/src/index.scss';
|
||||
```
|
||||
This file contains style modifications for all paragon components. It is not large and is the simplest way to include Paragon styles. Note
|
||||
|
||||
Paragon's production build ships with two different export targets:
|
||||
* It does not include bootstrap. You must include that yourself.
|
||||
* It must be included after bootstrap functions, variables, and mixins
|
||||
|
||||
**`themeable`**: This is the default export intended for use within Bootstrap 4 pages. Components ship with stock Bootstrap classnames and can accept Bootstrap themes. This build assumes Bootstrap/Font Awesome are already available on the consuming page. You should not need to add any additional stylesheets -- just plug and go. Import syntax is as follows:
|
||||
|
||||
`import { ComponentName } from '@edx/paragon';`
|
||||
If you are not using SCSS you can include a minified CSS file that also does not include bootstrap:
|
||||
```
|
||||
import '~@edx/paragon/dist/paragon.min.css';
|
||||
```
|
||||
|
||||
**`static`**: The static build is intended for use within legacy pages not styled with Bootstrap 4. Component classnames are namespaced with the `paragon__` prefix so as not to conflict with existing classnames defined elsewhere on the page. Be cognizant of poorly scoped legacy rules (e.g. a rule for all `input` or `h1` tags), which can still affect Paragon components. This build comes with its own stylesheet, available at `/static/paragon.min.css`. You must include this stylesheet on any page that uses Paragon components. Components can be imported thus:
|
||||
### Legacy Export Target [Removed]
|
||||
|
||||
**`static [Removed in version 4.0]`**: The static build is intended for use within legacy pages not styled with Bootstrap 4. Component classnames are namespaced with the `paragon__` prefix so as not to conflict with existing classnames defined elsewhere on the page. Be cognizant of poorly scoped legacy rules (e.g. a rule for all `input` or `h1` tags), which can still affect Paragon components. This build comes with its own stylesheet, available at `/static/paragon.min.css`. You must include this stylesheet on any page that uses Paragon components. Components can be imported thus:
|
||||
|
||||
`import { ComponentName } from '@edx/paragon/static';`
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
module.exports = 'test-file-stub';
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
6. Removal of CSS Module Support
|
||||
--------------------------------
|
||||
|
||||
Status
|
||||
------
|
||||
|
||||
Accepted
|
||||
|
||||
Context
|
||||
-------
|
||||
|
||||
Paragon utilizes CSS modules to create a namespaced version of Paragon components. This namespaced version included an export of bootstrap and paragon styles with the .paragon__ prefix. It's purpose was to allow Paragon to be used in pages that didn't include bootstrap and would like have conflicts if it was added. This "static" version is consumed in edx-platform in 11 places today.
|
||||
|
||||
A resulting complication of using CSS Modules is that each component JSX file must include a SCSS file that contains all the classes it will use. In most situations this is perfectly fine, but in our case we are including partials from bootstrap that contain variable names and mixins that have changed between minor versions. The result is that if a consuming application is using a 4.1.x version of bootstrap and Paragon's latest export is 4.2.x, there may be SCSS compile errors in the consuming app.
|
||||
|
||||
Decision
|
||||
--------
|
||||
|
||||
Remove our usage of CSS Modules. If we find a need to have CSS modules in the future we should look at other solutions. Concretely this decision includes these changes:
|
||||
|
||||
* Remove all SCSS imports in Paragon components (JSX).
|
||||
* Delete SCSS files that had no other purpose than to supply namespaced class names
|
||||
|
||||
Consequences
|
||||
------------
|
||||
|
||||
Paragon will rely on bootstrap class names being present for styling. It will no longer attempt to solve CSS conflicts for consuming applications.
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
* https://css-tricks.com/css-modules-part-1-need/
|
||||
File diff suppressed because it is too large
Load Diff
137
package.json
137
package.json
|
|
@ -2,21 +2,25 @@
|
|||
"name": "@edx/paragon",
|
||||
"version": "0.0.0-development",
|
||||
"description": "Accessible, responsive UI component library based on Bootstrap.",
|
||||
"main": "themeable/index.js",
|
||||
"main": "dist/paragon.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/edx/paragon.git"
|
||||
},
|
||||
"files": [
|
||||
"/dist",
|
||||
"/src"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production webpack --config webpack.config.js",
|
||||
"build": "webpack --mode production",
|
||||
"build-storybook": "build-storybook",
|
||||
"commit": "commit",
|
||||
"coveralls": "cat ./coverage/lcov.info | coveralls",
|
||||
"debug-test": "node --inspect-brk node_modules/.bin/jest --runInBand --coverage",
|
||||
"deploy-storybook": "storybook-to-ghpages",
|
||||
"deploy-storybook-ci": "storybook-to-ghpages --ci",
|
||||
"gc": "commit",
|
||||
"is-es5": "es-check es5 ./themeable/*.js ./static/*.js",
|
||||
"is-es5": "es-check es5 ./dist/*.js",
|
||||
"lint": "eslint --ext .js --ext .jsx .",
|
||||
"prepublishOnly": "npm run build",
|
||||
"semantic-release": "semantic-release",
|
||||
|
|
@ -26,80 +30,70 @@
|
|||
"travis-deploy-once": "travis-deploy-once"
|
||||
},
|
||||
"dependencies": {
|
||||
"@edx/edx-bootstrap": "^1.0.0",
|
||||
"@sambego/storybook-styles": "^1.0.0",
|
||||
"airbnb-prop-types": "^2.10.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"classnames": "^2.2.5",
|
||||
"email-prop-type": "^1.1.5",
|
||||
"airbnb-prop-types": "^2.12.0",
|
||||
"classnames": "^2.2.6",
|
||||
"email-prop-type": "^3.0.0",
|
||||
"font-awesome": "^4.7.0",
|
||||
"mailto-link": "^1.0.0",
|
||||
"prop-types": "^15.5.8",
|
||||
"react": "^16.4.2",
|
||||
"react-dom": "^16.1.0",
|
||||
"react-element-proptypes": "^1.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.8.4",
|
||||
"react-dom": "^16.8.4",
|
||||
"react-proptype-conditional-require": "^1.0.4",
|
||||
"react-responsive": "^5.0.0",
|
||||
"sanitize-html": "^1.18.2"
|
||||
"react-responsive": "^6.1.1",
|
||||
"sanitize-html": "^1.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^6.0.2",
|
||||
"@commitlint/config-angular": "^6.0.2",
|
||||
"@commitlint/prompt": "^6.0.2",
|
||||
"@commitlint/prompt-cli": "^6.0.2",
|
||||
"@storybook/addon-a11y": "^3.3.13",
|
||||
"@storybook/addon-actions": "^3.4.6",
|
||||
"@storybook/addon-centered": "^3.3.13",
|
||||
"@storybook/addon-console": "^1.0.0",
|
||||
"@storybook/addon-info": "^3.3.14",
|
||||
"@storybook/addon-options": "^3.3.10",
|
||||
"@storybook/addon-storyshots": "^3.3.10",
|
||||
"@storybook/addons": "^3.3.10",
|
||||
"@storybook/channels": "^3.3.10",
|
||||
"@storybook/react": "^3.3.10",
|
||||
"@storybook/storybook-deployer": "^2.3.0",
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-jest": "^23.0.0",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
||||
"babel-preset-env": "^1.4.0",
|
||||
"@babel/core": "^7.3.4",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.3.4",
|
||||
"@babel/preset-env": "^7.3.4",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@commitlint/cli": "^7.5.2",
|
||||
"@commitlint/config-angular": "^7.5.0",
|
||||
"@commitlint/prompt-cli": "^7.5.0",
|
||||
"@edx/edx-bootstrap": "^1.0.4",
|
||||
"@storybook/addon-a11y": "^5.0.3",
|
||||
"@storybook/addon-actions": "^5.0.3",
|
||||
"@storybook/addon-centered": "^5.0.3",
|
||||
"@storybook/addon-console": "^1.1.0",
|
||||
"@storybook/addon-info": "^5.0.3",
|
||||
"@storybook/addon-storyshots": "^5.0.3",
|
||||
"@storybook/react": "^5.0.3",
|
||||
"@storybook/storybook-deployer": "^2.8.1",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^24.5.0",
|
||||
"babel-loader": "^8.0.5",
|
||||
"babel-plugin-require-context-hook": "^1.0.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"coveralls": "^3.0.0",
|
||||
"css-loader": "^0.28.4",
|
||||
"enzyme": "^3.1.1",
|
||||
"enzyme-adapter-react-16": "^1.0.4",
|
||||
"es-check": "^3.0.0",
|
||||
"eslint": "^4.5.0",
|
||||
"eslint-config-edx": "^4.0.3",
|
||||
"eslint-import-resolver-webpack": "^0.10.0",
|
||||
"eslint-plugin-compat": "^2.2.0",
|
||||
"extract-text-webpack-plugin": "^3.0.1",
|
||||
"file-loader": "^1.1.4",
|
||||
"greenkeeper-lockfile": "^1.7.1",
|
||||
"husky": "^1.0.0",
|
||||
"bootstrap": "^4.3.1",
|
||||
"clean-webpack-plugin": "^2.0.1",
|
||||
"coveralls": "^3.0.3",
|
||||
"css-loader": "^2.1.1",
|
||||
"enzyme": "^3.9.0",
|
||||
"enzyme-adapter-react-16": "^1.11.2",
|
||||
"es-check": "^5.0.0",
|
||||
"eslint-config-edx": "^4.0.4",
|
||||
"greenkeeper-lockfile": "^1.15.1",
|
||||
"husky": "^1.3.1",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^22.0.4",
|
||||
"jest-cli": "^22.0.4",
|
||||
"jest": "^24.5.0",
|
||||
"markdown-loader-jest": "^0.1.1",
|
||||
"node-sass": "^4.9.4",
|
||||
"postcss-scss": "^2.0.0",
|
||||
"react-router-dom": "^4.1.1",
|
||||
"react-test-renderer": "^16.1.0",
|
||||
"sass-loader": "^6.0.5",
|
||||
"semantic-release": "^15.1.7",
|
||||
"source-map-loader": "^0.2.1",
|
||||
"storybook-readme": "^3.3.0",
|
||||
"style-loader": "^0.22.0",
|
||||
"travis-deploy-once": "^5.0.0",
|
||||
"uglifyjs-webpack-plugin": "^1.3.0",
|
||||
"webpack": "^3.0.0",
|
||||
"webpack-build-notifier": "^0.1.22",
|
||||
"webpack-dev-server": "^3.0.0"
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"node-sass": "^4.11.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"semantic-release": "^15.13.3",
|
||||
"source-map-loader": "^0.2.4",
|
||||
"storybook-readme": "^4.0.5",
|
||||
"style-loader": "^0.23.1",
|
||||
"travis-deploy-once": "^5.0.11",
|
||||
"uglifyjs-webpack-plugin": "^2.1.2",
|
||||
"webpack": "^4.29.6",
|
||||
"webpack-cli": "^3.3.0"
|
||||
},
|
||||
"jest": {
|
||||
"transform": {
|
||||
"^.+\\.md?$": "markdown-loader-jest",
|
||||
"^.+\\.jsx?$": "<rootDir>/node_modules/babel-jest"
|
||||
"^.+\\.jsx?$": "babel-jest"
|
||||
},
|
||||
"setupFiles": [
|
||||
"./src/setupTest.js"
|
||||
|
|
@ -108,14 +102,17 @@
|
|||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
|
||||
"\\.(css|scss)$": "identity-obj-proxy"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.{js,jsx}"
|
||||
],
|
||||
"coveragePathIgnorePatterns": [
|
||||
"/.storybook/",
|
||||
"/node_modules/",
|
||||
"(.stories)\\.(jsx)$",
|
||||
"\\.(mock.js)$"
|
||||
"src/setupTest.js",
|
||||
"src/index.js",
|
||||
"/tests/"
|
||||
],
|
||||
"transformIgnorePatterns": [
|
||||
"/node_modules/(?!lodash-es/.*)"
|
||||
"/node_modules/(?!(@edx/paragon)/).*/"
|
||||
]
|
||||
},
|
||||
"browserslist": [
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
@import "~bootstrap/scss/_type";
|
||||
@import "~bootstrap/scss/_utilities";
|
||||
|
|
@ -1,10 +1,5 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import Breadcrumb from './index';
|
||||
import README from './README.md';
|
||||
|
|
@ -26,10 +21,7 @@ const sampleLinks = [
|
|||
];
|
||||
|
||||
storiesOf('Breadcrumb', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<Breadcrumb links={sampleLinks} />
|
||||
))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
# Breadcrumb
|
||||
|
||||
Provides a basic breadcrumb component.
|
||||
|
||||
## API
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import styles from './Breadcrumb.scss';
|
||||
import Icon from '../Icon';
|
||||
|
||||
const Breadcrumbs = ({
|
||||
|
|
@ -12,20 +11,20 @@ const Breadcrumbs = ({
|
|||
|
||||
return (
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol className={classNames(styles['list-inline'])}>
|
||||
<ol className={classNames('list-inline')}>
|
||||
{links.map(({ url, label }, i) => (
|
||||
<React.Fragment key={url}>
|
||||
<li className={classNames(styles['list-inline-item'])}>
|
||||
<li className={classNames('list-inline-item')}>
|
||||
<a href={url} {...(clickHandler && { onClick: clickHandler })}>{label}</a>
|
||||
</li>
|
||||
{(activeLabel || ((i + 1) < linkCount)) &&
|
||||
<li className={classNames(styles['list-inline-item'])} role="presentation" aria-label="spacer">
|
||||
<li className={classNames('list-inline-item')} role="presentation" aria-label="spacer">
|
||||
{spacer || <Icon className={['fa', 'fa-chevron-right']} id={`spacer-${i}`} />}
|
||||
</li>
|
||||
}
|
||||
</React.Fragment>
|
||||
))}
|
||||
{activeLabel && <li className={classNames(styles['list-inline-item'])} key="active">{activeLabel}</li>}
|
||||
{activeLabel && <li className={classNames('list-inline-item')} key="active">{activeLabel}</li>}
|
||||
</ol>
|
||||
</nav>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
@import "~bootstrap/scss/_buttons";
|
||||
@import "~bootstrap/scss/_close";
|
||||
|
|
@ -1,20 +1,12 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
import README from './README.md';
|
||||
|
||||
import Button from './index';
|
||||
|
||||
storiesOf('Button', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<Button
|
||||
label="Click me and check the console!"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Button from './index';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './Button.scss';
|
||||
|
||||
class Button extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -63,12 +62,12 @@ class Button extends React.Component {
|
|||
<button
|
||||
{...other}
|
||||
className={classNames([
|
||||
styles.btn,
|
||||
'btn',
|
||||
...className,
|
||||
], {
|
||||
[styles[`btn-${buttonType}`]]: buttonType !== undefined,
|
||||
[`btn-${buttonType}`]: buttonType !== undefined,
|
||||
}, {
|
||||
[styles.close]: isClose,
|
||||
close: isClose,
|
||||
})}
|
||||
onBlur={this.onBlur}
|
||||
onClick={this.onClick}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* eslint-disable no-console */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import CheckBox from './index';
|
||||
import Button from '../Button';
|
||||
|
|
@ -47,10 +41,7 @@ class CheckBoxWrapper extends React.Component {
|
|||
}
|
||||
|
||||
storiesOf('CheckBox', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<CheckBox
|
||||
name="checkbox"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import CheckBox from './index';
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
@import "~bootstrap/scss/_forms";
|
||||
@import "~bootstrap/scss/mixins/_forms";
|
||||
|
|
@ -1,11 +1,5 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* eslint-disable no-console */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import CheckBoxGroup from './index';
|
||||
import CheckBox from '../CheckBox';
|
||||
|
|
@ -13,10 +7,7 @@ import CheckBox from '../CheckBox';
|
|||
import README from './README.md';
|
||||
|
||||
storiesOf('CheckBoxGroup', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<CheckBoxGroup>
|
||||
<CheckBox
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import CheckBoxGroup from './index';
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ElementPropTypes from 'react-element-proptypes';
|
||||
|
||||
import CheckBox from '../CheckBox';
|
||||
import styles from './CheckBoxGroup.scss';
|
||||
|
||||
function CheckBoxGroup(props) {
|
||||
return (
|
||||
<div className={styles['form-group']}>
|
||||
<div className="form-group">
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
CheckBoxGroup.propTypes = {
|
||||
children: PropTypes.arrayOf(ElementPropTypes.elementOfType(CheckBox)).isRequired,
|
||||
children: PropTypes.arrayOf(PropTypes.element).isRequired,
|
||||
};
|
||||
|
||||
export default CheckBoxGroup;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
@import "~bootstrap/scss/_variables";
|
||||
@import "~bootstrap/scss/_grid";
|
||||
@import "~bootstrap/scss/_utilities";
|
||||
|
||||
// Local Variables
|
||||
$collapsible-border: 1px solid silver;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,12 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
import styles from '@sambego/storybook-styles';
|
||||
import README from './README.md';
|
||||
|
||||
import Collapsible from './index';
|
||||
import { breakpoints } from '../Responsive';
|
||||
|
||||
storiesOf('Collapsible', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addDecorator(styles({
|
||||
height: '500px',
|
||||
margin: '250px',
|
||||
}))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage without resizing', () => (
|
||||
<Collapsible title="Click me to expand">
|
||||
<p>Your stuff goes here</p>
|
||||
|
|
|
|||
|
|
@ -31,23 +31,12 @@ resize to determine whether to display the collapsible or regular view. The
|
|||
example below demonstrates a collapsible that will only show the open/close
|
||||
button for non-desktop screens.
|
||||
|
||||
If no function is given, the collapsible does not handle resizing and will
|
||||
always show the open/close button.
|
||||
|
||||
### `iconId` (string; optional)
|
||||
`iconId` is the id attribute that is passed to the icon on the collapsible.
|
||||
Defaults to the empty string.
|
||||
|
||||
```jsx
|
||||
<Collapsible
|
||||
title="Collapsible"
|
||||
expandedTitle={<h2>Collapsible</h2>}
|
||||
isCollapsible={() => window.matchMedia('(min-width: 992px)').matches}
|
||||
>
|
||||
<p>Child 1</p>
|
||||
<p>Child 2</p>
|
||||
</Collapsible>
|
||||
```
|
||||
|
||||
If no function is given, the collapsible does not handle resizing and will
|
||||
always show the open/close button.
|
||||
|
||||
### `onToggle` (function; optional)
|
||||
`onToggle` is an optional callback that is trigged when the Collapsible components is opened or closed. A boolean is passed to the callback with the value of `isOpen` from the component's state.
|
||||
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
|
|||
import Button from '../Button';
|
||||
import Icon from '../Icon';
|
||||
|
||||
import styles from './Collapsible.scss';
|
||||
|
||||
class Collapsible extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -73,11 +72,11 @@ class Collapsible extends React.Component {
|
|||
const { isExpanded, isOpen } = this.state;
|
||||
|
||||
return (
|
||||
<div className={[classNames(
|
||||
<div className={classNames(
|
||||
'collapsible',
|
||||
{ [styles.open]: isOpen && !isExpanded },
|
||||
{ [styles.expanded]: isExpanded },
|
||||
)]}
|
||||
{ open: isOpen && !isExpanded },
|
||||
{ expanded: isExpanded },
|
||||
)}
|
||||
>
|
||||
{isExpanded ? (
|
||||
expandedTitle
|
||||
|
|
@ -86,8 +85,8 @@ class Collapsible extends React.Component {
|
|||
aria-expanded={isOpen}
|
||||
className={[classNames(
|
||||
'btn-block text-left',
|
||||
styles['btn-collapsible'],
|
||||
{ [styles.open]: isOpen },
|
||||
'btn-collapsible',
|
||||
{ open: isOpen },
|
||||
)]}
|
||||
label={
|
||||
<div className="collapsible-title">
|
||||
|
|
@ -107,10 +106,10 @@ class Collapsible extends React.Component {
|
|||
onClick={this.handleClick}
|
||||
/>
|
||||
)}
|
||||
<div className={[classNames(
|
||||
<div className={classNames(
|
||||
'collapsible-body',
|
||||
{ [styles.open]: isOpen || isExpanded },
|
||||
)]}
|
||||
{ open: isOpen || isExpanded },
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
@import "~bootstrap/scss/_reboot";
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './CssJail.scss';
|
||||
|
||||
function CssJail({ children }) {
|
||||
return (
|
||||
<div
|
||||
style={{ fontFamily: 'sans-serif' }}
|
||||
className={styles['css-jail']}
|
||||
className="css-jail"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,3 @@
|
|||
@import "~bootstrap/scss/_dropdown";
|
||||
@import "~bootstrap/scss/utilities/_borders";
|
||||
@import "~bootstrap/scss/utilities/_display";
|
||||
@import "~bootstrap/scss/utilities/_flex";
|
||||
@import "~bootstrap/scss/utilities/_background";
|
||||
|
||||
// Trying to patch this upstream, then this can
|
||||
// be removed.
|
||||
// https://github.com/twbs/bootstrap/pull/23990
|
||||
.show {
|
||||
> a:focus {
|
||||
outline-width: 2px;
|
||||
outline-style: solid;
|
||||
outline-color: Highlight;
|
||||
outline: -webkit-focus-ring-color auto 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown.has-icon {
|
||||
.icon-container {
|
||||
max-width: 40px;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,12 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import Dropdown from './index';
|
||||
import Icon from '../Icon';
|
||||
import README from './README.md';
|
||||
|
||||
storiesOf('Dropdown', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<Dropdown
|
||||
title="Search Engines"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './Dropdown.scss';
|
||||
import Button from '../Button';
|
||||
|
||||
export const triggerKeys = {
|
||||
|
|
@ -107,7 +106,7 @@ class Dropdown extends React.Component {
|
|||
if (React.isValidElement(menuItem)) {
|
||||
const cloneProps = {
|
||||
ref: (item) => { this.menuItems[i] = item; },
|
||||
className: styles['dropdown-item'],
|
||||
className: 'dropdown-item',
|
||||
key: i,
|
||||
onKeyDown: this.handleMenuKeyDown,
|
||||
};
|
||||
|
|
@ -115,9 +114,9 @@ class Dropdown extends React.Component {
|
|||
}
|
||||
return (
|
||||
<a
|
||||
className={styles['dropdown-item']}
|
||||
className="dropdown-item"
|
||||
href={menuItem.href}
|
||||
key={i}
|
||||
key={menuItem.href}
|
||||
onKeyDown={this.handleMenuKeyDown}
|
||||
ref={(item) => {
|
||||
this.menuItems[i] = item;
|
||||
|
|
@ -143,31 +142,31 @@ class Dropdown extends React.Component {
|
|||
return (
|
||||
<div
|
||||
className={classNames([
|
||||
styles.dropdown,
|
||||
'dropdown',
|
||||
{
|
||||
[styles.show]: open,
|
||||
[styles['has-icon']]: hasIconElement,
|
||||
[styles.rounded]: hasIconElement,
|
||||
[styles.border]: hasIconElement,
|
||||
[styles['d-flex']]: hasIconElement,
|
||||
[styles['bg-white']]: hasIconElement,
|
||||
show: open,
|
||||
'has-icon': hasIconElement,
|
||||
rounded: hasIconElement,
|
||||
border: hasIconElement,
|
||||
'd-flex': hasIconElement,
|
||||
'bg-white': hasIconElement,
|
||||
},
|
||||
])}
|
||||
ref={(container) => { this.container = container; }}
|
||||
>
|
||||
{ hasIconElement &&
|
||||
<div
|
||||
className={[classNames([
|
||||
styles['icon-container'],
|
||||
styles['d-flex'],
|
||||
styles['align-items-center'],
|
||||
styles['justify-content-center'],
|
||||
styles['border-right'],
|
||||
])]}
|
||||
className={classNames([
|
||||
'icon-container',
|
||||
'd-flex',
|
||||
'align-items-center',
|
||||
'justify-content-center',
|
||||
'border-right',
|
||||
])}
|
||||
>
|
||||
{React.cloneElement(iconElement, {
|
||||
className: iconElement.props && Array.isArray(iconElement.props.className) ?
|
||||
[...iconElement.props.className, styles['rounded-left']] : styles['rounded-left'],
|
||||
[...iconElement.props.className, 'rounded-left'] : 'rounded-left',
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
|
|
@ -179,11 +178,11 @@ class Dropdown extends React.Component {
|
|||
onClick={this.toggle}
|
||||
onKeyDown={this.handleToggleKeyDown}
|
||||
className={[classNames([
|
||||
styles['dropdown-toggle'],
|
||||
'dropdown-toggle',
|
||||
{
|
||||
[styles['border-0']]: hasIconElement,
|
||||
[styles['rounded-0']]: hasIconElement,
|
||||
[styles['bg-white']]: hasIconElement,
|
||||
'border-0': hasIconElement,
|
||||
'rounded-0': hasIconElement,
|
||||
'bg-white': hasIconElement,
|
||||
},
|
||||
])]}
|
||||
type="button"
|
||||
|
|
@ -193,8 +192,8 @@ class Dropdown extends React.Component {
|
|||
aria-label={title}
|
||||
aria-hidden={!open}
|
||||
className={classNames([
|
||||
styles['dropdown-menu'],
|
||||
{ [styles.show]: open },
|
||||
'dropdown-menu',
|
||||
{ show: open },
|
||||
])}
|
||||
role="menu"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
@import "~bootstrap/scss/_forms";
|
||||
@import '~bootstrap/scss/utilities/_borders';
|
||||
@import "~bootstrap/scss/utilities/_screenreaders.scss";
|
||||
@import '~bootstrap/scss/utilities/_spacing';
|
||||
|
||||
.paragon-fieldset {
|
||||
@extend .mb-4;
|
||||
margin-bottom: $spacer * 1.5;
|
||||
|
||||
.form-control {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
fieldset legend {
|
||||
width: auto;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import Fieldset from './index';
|
||||
import InputText from '../InputText/index';
|
||||
|
|
@ -75,10 +71,7 @@ class ValidatedForm extends React.Component {
|
|||
}
|
||||
|
||||
storiesOf('Fieldset', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<form>
|
||||
<Fieldset
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import newId from '../utils/newId';
|
||||
import styles from './Fieldset.scss';
|
||||
|
||||
import ValidationMessage from '../ValidationMessage/index';
|
||||
import Variant from '../utils/constants';
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ class Fieldset extends React.Component {
|
|||
|
||||
switch (variant.status) {
|
||||
case Variant.status.INFO:
|
||||
className = styles['is-invalid-nodanger'];
|
||||
className = 'is-invalid-nodanger';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -73,18 +73,18 @@ class Fieldset extends React.Component {
|
|||
} = this.props;
|
||||
const errorId = `error-${this.state.id}`;
|
||||
return (
|
||||
<div className={styles['paragon-fieldset']}>
|
||||
<div className="paragon-fieldset">
|
||||
<fieldset
|
||||
className={classNames(
|
||||
styles['form-control'],
|
||||
styles['p-3'],
|
||||
'form-control',
|
||||
'p-3',
|
||||
{ 'is-invalid': !isValid },
|
||||
this.getVariantClassName(),
|
||||
className,
|
||||
)}
|
||||
aria-describedby={errorId}
|
||||
>
|
||||
<legend className={styles['p-1']}>{legend}</legend>
|
||||
<legend className="p-1">{legend}</legend>
|
||||
{children}
|
||||
</fieldset>
|
||||
<ValidationMessage
|
||||
|
|
|
|||
|
|
@ -1,23 +1,13 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies, no-console */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { setConsoleOptions } from '@storybook/addon-console';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import 'font-awesome/css/font-awesome.min.css';
|
||||
|
||||
import README from './README.md';
|
||||
|
||||
import Hyperlink from './index';
|
||||
import Icon from '../Icon/index';
|
||||
|
||||
setConsoleOptions({
|
||||
panelExclude: ['warn', 'error'],
|
||||
});
|
||||
|
||||
const onClick = (event) => {
|
||||
console.log(`onClick fired for ${event.target}`);
|
||||
|
|
@ -26,10 +16,7 @@ const onClick = (event) => {
|
|||
};
|
||||
|
||||
storiesOf('HyperLink', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('minimal usage', () => <Hyperlink destination="https://en.wikipedia.org/wiki/Hyperlink" content="edX.org" />)
|
||||
.add('with blank target', () => (
|
||||
<Hyperlink
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { shallow, mount } from 'enzyme';
|
||||
import classNames from 'classnames';
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
|
||||
import Hyperlink from './index';
|
||||
|
||||
|
|
@ -47,7 +45,7 @@ describe('correct rendering', () => {
|
|||
|
||||
expect(icon.prop('aria-hidden')).toEqual(false);
|
||||
expect(icon.prop('className'))
|
||||
.toEqual(classNames(FontAwesomeStyles.fa, FontAwesomeStyles['fa-external-link']));
|
||||
.toEqual(classNames('fa', 'fa-external-link'));
|
||||
expect(icon.prop('aria-label')).toEqual(externalLinkAlternativeText);
|
||||
expect(icon.prop('title')).toEqual(externalLinkTitle);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
import PropTypes from 'prop-types';
|
||||
import isRequiredIf from 'react-proptype-conditional-require';
|
||||
|
||||
|
|
@ -22,7 +21,7 @@ function Hyperlink(props) {
|
|||
// Space between content and icon
|
||||
<span>{' '}
|
||||
<span
|
||||
className={classNames(FontAwesomeStyles.fa, FontAwesomeStyles['fa-external-link'])}
|
||||
className={classNames('fa', 'fa-external-link')}
|
||||
aria-hidden={false}
|
||||
aria-label={externalLinkAlternativeText}
|
||||
title={externalLinkTitle}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
@import "~bootstrap/scss/utilities/_screenreaders.scss";
|
||||
|
|
@ -1,36 +1,20 @@
|
|||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
import Icon from './index';
|
||||
import README from './README.md';
|
||||
|
||||
storiesOf('Icon', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<Icon
|
||||
className={[
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-birthday-cake'],
|
||||
FontAwesomeStyles['fa-4x'],
|
||||
]}
|
||||
className={['fa', 'fa-birthday-cake', 'fa-4x']}
|
||||
/>
|
||||
))
|
||||
.add('with screenreader text', () => (
|
||||
<Icon
|
||||
id="SampleIcon"
|
||||
className={[
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-smile-o'],
|
||||
FontAwesomeStyles['fa-4x'],
|
||||
]}
|
||||
className={['fa', 'fa-smile-o', 'fa-4x']}
|
||||
screenReaderText="Happy Icon"
|
||||
/>
|
||||
));
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
import Icon from './index';
|
||||
|
||||
const testId = 'testId';
|
||||
const classNames = [
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-check'],
|
||||
'fa',
|
||||
'fa-check',
|
||||
];
|
||||
const srTest = 'srTest';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './Icon.scss';
|
||||
import newId from '../utils/newId';
|
||||
|
||||
function Icon(props) {
|
||||
|
|
@ -14,7 +13,7 @@ function Icon(props) {
|
|||
aria-hidden={props.hidden}
|
||||
/>
|
||||
{ props.screenReaderText &&
|
||||
<span className={classNames(styles['sr-only'])}>
|
||||
<span className={classNames('sr-only')}>
|
||||
{props.screenReaderText}
|
||||
</span>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,11 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import InputSelect from './index';
|
||||
import README from './README.md';
|
||||
|
||||
storiesOf('InputSelect', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<InputSelect
|
||||
name="fruits"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
|
||||
import Button from '../Button';
|
||||
import Icon from '../Icon';
|
||||
|
|
@ -49,10 +43,7 @@ class FocusInputWrapper extends React.Component {
|
|||
|
||||
|
||||
storiesOf('InputText', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('minimal usage', () => (
|
||||
<InputText
|
||||
name="name"
|
||||
|
|
@ -269,8 +260,8 @@ storiesOf('InputText', module)
|
|||
<Icon
|
||||
id="checkmark"
|
||||
className={[
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-check'],
|
||||
'fa',
|
||||
'fa-check',
|
||||
]}
|
||||
screenReaderText="Checkmark"
|
||||
/>
|
||||
|
|
@ -290,8 +281,8 @@ storiesOf('InputText', module)
|
|||
<Icon
|
||||
id="checkmark"
|
||||
className={[
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-check'],
|
||||
'fa',
|
||||
'fa-check',
|
||||
]}
|
||||
screenReaderText="Checkmark"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
@import 'bootstrap/scss/_list-group.scss';
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import 'bootstrap/scss/utilities/_screenreaders.scss';
|
||||
|
||||
import Button from '../Button';
|
||||
import ListBox from '../ListBox';
|
||||
|
|
@ -127,7 +124,6 @@ class ListBoxWrapperForSelectedOptionIndex extends React.Component {
|
|||
}
|
||||
|
||||
storiesOf('ListBox', module)
|
||||
.addDecorator(centered)
|
||||
.add('basic usage', () => (
|
||||
<ListBox>
|
||||
<ListBoxOption>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import classNames from 'classnames';
|
||||
import { elementType, nonNegativeInteger, or } from 'airbnb-prop-types';
|
||||
import { nonNegativeInteger } from 'airbnb-prop-types';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import ListBoxOption from '../ListBoxOption';
|
||||
import styles from './ListBox.scss';
|
||||
|
||||
export default class ListBox extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -91,7 +89,7 @@ export default class ListBox extends React.Component {
|
|||
this.props.tag,
|
||||
{
|
||||
'aria-activedescendant': this.state.selectedOptionIndex === null ? null : `list-box-option-${this.state.selectedOptionIndex}`,
|
||||
className: classNames([styles['list-group'], this.props.className]),
|
||||
className: classNames(['list-group', this.props.className]),
|
||||
onFocus: this.onFocus,
|
||||
onKeyDown: this.onKeyDown,
|
||||
role: 'listbox',
|
||||
|
|
@ -104,10 +102,7 @@ export default class ListBox extends React.Component {
|
|||
}
|
||||
|
||||
ListBox.propTypes = {
|
||||
children: or([
|
||||
elementType(ListBoxOption),
|
||||
PropTypes.arrayOf(elementType(ListBoxOption)),
|
||||
]).isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
className: PropTypes.string,
|
||||
selectedOptionIndex: nonNegativeInteger,
|
||||
tag: PropTypes.string,
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
@import 'bootstrap/scss/_list-group.scss';
|
||||
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './ListBoxOption.scss';
|
||||
|
||||
export default class ListBoxOption extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -45,8 +44,8 @@ export default class ListBoxOption extends React.Component {
|
|||
{
|
||||
'aria-selected': isSelected,
|
||||
className: classNames(
|
||||
styles['list-group-item'],
|
||||
styles['list-group-item-action'],
|
||||
'list-group-item',
|
||||
'list-group-item-action',
|
||||
{
|
||||
active: this.props.isSelected,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,19 +2,10 @@
|
|||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { setConsoleOptions } from '@storybook/addon-console';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import MailtoLink from './index';
|
||||
import README from './README.md';
|
||||
|
||||
setConsoleOptions({
|
||||
panelExclude: ['warn', 'error'],
|
||||
});
|
||||
|
||||
const onClick = (event) => {
|
||||
console.log(`onClick fired for ${event.target}`);
|
||||
|
||||
|
|
@ -22,10 +13,7 @@ const onClick = (event) => {
|
|||
};
|
||||
|
||||
storiesOf('MailtoLink', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('minimal usage', () => (
|
||||
<MailtoLink
|
||||
to="edx@example.com"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
@import "~bootstrap/scss/_variables";
|
||||
@import "~bootstrap/scss/_modal";
|
||||
@import "~bootstrap/scss/_grid";
|
||||
@import "~bootstrap/scss/_utilities";
|
||||
|
||||
.modal.show {
|
||||
position: fixed;
|
||||
background-color: transparent;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
import { action } from '@storybook/addon-actions';
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import Modal from './index';
|
||||
import Button from '../Button';
|
||||
|
|
@ -67,10 +62,7 @@ ModalWrapper.defaultProps = {
|
|||
};
|
||||
|
||||
storiesOf('Modal', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<Modal
|
||||
open
|
||||
|
|
@ -147,8 +139,8 @@ storiesOf('Modal', module)
|
|||
closeText={
|
||||
<Icon
|
||||
className={[
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-ship'],
|
||||
'fa',
|
||||
'fa-ship',
|
||||
]}
|
||||
screenReaderText="Close"
|
||||
/>}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ import ReactDOM from 'react-dom';
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
import styles from './Modal.scss';
|
||||
import Button, { buttonPropTypes } from '../Button';
|
||||
import Icon from '../Icon';
|
||||
import newId from '../utils/newId';
|
||||
|
|
@ -74,10 +72,10 @@ class Modal extends React.Component {
|
|||
switch (variant.status) {
|
||||
case Variant.status.WARNING:
|
||||
variantIconClassName = classNames(
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-exclamation-triangle'],
|
||||
FontAwesomeStyles['fa-3x'],
|
||||
styles[`text-${variant.status.toLowerCase()}`],
|
||||
'fa',
|
||||
'fa-exclamation-triangle',
|
||||
'fa-3x',
|
||||
`text-${variant.status.toLowerCase()}`,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -91,14 +89,14 @@ class Modal extends React.Component {
|
|||
const { variant } = this.props;
|
||||
|
||||
return (
|
||||
<div className={styles['container-fluid']}>
|
||||
<div className={styles.row}>
|
||||
<div className={styles['col-md-10']}>
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-10">
|
||||
<div>
|
||||
{body}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['col-md-2']}>
|
||||
<div className="col-md-2">
|
||||
<Icon
|
||||
id={newId(`Modal-${variant.status}`)}
|
||||
className={[
|
||||
|
|
@ -139,21 +137,17 @@ class Modal extends React.Component {
|
|||
}
|
||||
|
||||
renderButtons() {
|
||||
return this.props.buttons.map((button, i) => {
|
||||
let buttonElement = button;
|
||||
let buttonProps = button.props;
|
||||
return this.props.buttons.map((button) => {
|
||||
// button is either a Button component that we want clone or a set of props
|
||||
const buttonProps = button.type === Button ? button.props : button;
|
||||
|
||||
if (button.type !== Button) {
|
||||
buttonProps = button;
|
||||
}
|
||||
|
||||
buttonElement = (<Button
|
||||
{...buttonProps}
|
||||
key={i}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
/>);
|
||||
|
||||
return buttonElement;
|
||||
return (
|
||||
<Button
|
||||
{...buttonProps}
|
||||
key={buttonProps.label}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -179,21 +173,21 @@ class Modal extends React.Component {
|
|||
<div>
|
||||
<div
|
||||
className={classNames({
|
||||
[styles['modal-backdrop']]: open,
|
||||
[styles.show]: open,
|
||||
[styles.fade]: !open,
|
||||
'modal-backdrop': open,
|
||||
show: open,
|
||||
fade: !open,
|
||||
})}
|
||||
role="presentation"
|
||||
/>
|
||||
<div
|
||||
className={classNames(
|
||||
styles.modal,
|
||||
'modal',
|
||||
'js-close-modal-on-click',
|
||||
{
|
||||
[styles.show]: open,
|
||||
[styles.fade]: !open,
|
||||
[styles['d-block']]: open,
|
||||
[styles['is-ie11']]: this.isIE11,
|
||||
show: open,
|
||||
fade: !open,
|
||||
'd-block': open,
|
||||
'is-ie11': this.isIE11,
|
||||
},
|
||||
)}
|
||||
role="presentation"
|
||||
|
|
@ -201,7 +195,7 @@ class Modal extends React.Component {
|
|||
>
|
||||
<div
|
||||
className={classNames({
|
||||
[styles['modal-dialog']]: open,
|
||||
'modal-dialog': open,
|
||||
})}
|
||||
role="dialog"
|
||||
aria-modal
|
||||
|
|
@ -209,9 +203,9 @@ class Modal extends React.Component {
|
|||
{...(!renderHeaderCloseButton ? { tabIndex: '-1' } : {})}
|
||||
{...(!renderHeaderCloseButton ? { ref: this.setFirstFocusableElement } : {})}
|
||||
>
|
||||
<div className={styles['modal-content']}>
|
||||
<div className={styles['modal-header']}>
|
||||
<h2 className={styles['modal-title']} id={this.headerId}>{this.props.title}</h2>
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h2 className="modal-title" id={this.headerId}>{this.props.title}</h2>
|
||||
{ renderHeaderCloseButton &&
|
||||
<Button
|
||||
label={<Icon className={['fa', 'fa-times', 'js-close-modal-on-click']} />}
|
||||
|
|
@ -223,10 +217,10 @@ class Modal extends React.Component {
|
|||
/>
|
||||
}
|
||||
</div>
|
||||
<div className={styles['modal-body']}>
|
||||
<div className="modal-body">
|
||||
{this.renderBody()}
|
||||
</div>
|
||||
<div className={styles['modal-footer']}>
|
||||
<div className="modal-footer">
|
||||
{this.renderButtons()}
|
||||
<Button
|
||||
label={this.props.closeText}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,3 @@
|
|||
@import "~bootstrap/scss/_pagination";
|
||||
@import "~bootstrap/scss/_buttons";
|
||||
@import "~bootstrap/scss/_variables";
|
||||
@import "~bootstrap/scss/utilities/_screenreaders.scss";
|
||||
@import "~bootstrap/scss/utilities/_spacing";
|
||||
@import "~bootstrap/scss/utilities/_borders";
|
||||
|
||||
.page-link {
|
||||
border-color: $pagination-border-color !important;
|
||||
border-radius: 0 !important;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,12 @@
|
|||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import Pagination from './index';
|
||||
import README from './README.md';
|
||||
|
||||
storiesOf('Pagination', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<Pagination
|
||||
paginationLabel="pagination navigation"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { between } from 'airbnb-prop-types';
|
||||
import classNames from 'classnames';
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
|
|
@ -10,8 +9,6 @@ import getTextFromElement from '../utils/getTextFromElement';
|
|||
import Icon from '../Icon';
|
||||
import newId from '../utils/newId';
|
||||
|
||||
import styles from './Pagination.scss';
|
||||
|
||||
class Pagination extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -83,15 +80,15 @@ class Pagination extends React.Component {
|
|||
renderEllipsisButton() {
|
||||
return (
|
||||
<li
|
||||
className={classNames([styles['page-item'], styles.disabled])}
|
||||
className={classNames(['page-item', 'disabled'])}
|
||||
key={newId('pagination-ellipsis-')}
|
||||
>
|
||||
<span
|
||||
className={classNames([
|
||||
styles.btn,
|
||||
styles['page-link'],
|
||||
styles['ml-0'],
|
||||
styles['border-0'],
|
||||
'btn',
|
||||
'page-link',
|
||||
'ml-0',
|
||||
'border-0',
|
||||
])}
|
||||
>
|
||||
...
|
||||
|
|
@ -120,15 +117,15 @@ class Pagination extends React.Component {
|
|||
return (
|
||||
<li
|
||||
className={classNames([
|
||||
styles['page-item'],
|
||||
'page-item',
|
||||
{
|
||||
[styles.active]: active,
|
||||
active,
|
||||
},
|
||||
])}
|
||||
key={page}
|
||||
>
|
||||
<Button
|
||||
className={[styles['page-link']]}
|
||||
className={['page-link']}
|
||||
aria-label={ariaLabel}
|
||||
label={page.toString()}
|
||||
inputRef={(element) => { this.pageRefs[page] = element; }}
|
||||
|
|
@ -163,15 +160,15 @@ class Pagination extends React.Component {
|
|||
|
||||
return (
|
||||
<li
|
||||
className={classNames([styles['page-item'], styles.disabled])}
|
||||
className={classNames(['page-item', 'disabled'])}
|
||||
key={currentPage}
|
||||
>
|
||||
<span
|
||||
className={classNames([
|
||||
styles.btn,
|
||||
styles['page-link'],
|
||||
styles['mx-2'],
|
||||
styles['border-0'],
|
||||
'btn',
|
||||
'page-link',
|
||||
'mx-2',
|
||||
'border-0',
|
||||
])}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
|
|
@ -203,23 +200,23 @@ class Pagination extends React.Component {
|
|||
return (
|
||||
<li
|
||||
className={classNames(
|
||||
styles['page-item'],
|
||||
'page-item',
|
||||
{
|
||||
[styles.disabled]: isFirstPage,
|
||||
disabled: isFirstPage,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
className={['previous', styles['page-link']]}
|
||||
className={['previous', 'page-link']}
|
||||
aria-label={ariaLabel}
|
||||
label={
|
||||
<div>
|
||||
<Icon
|
||||
id={newId('pagination-')}
|
||||
className={[
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-chevron-left'],
|
||||
styles['mr-2'],
|
||||
'fa',
|
||||
'fa-chevron-left',
|
||||
'mr-2',
|
||||
]}
|
||||
/>
|
||||
{buttonLabels.previous}
|
||||
|
|
@ -256,14 +253,14 @@ class Pagination extends React.Component {
|
|||
return (
|
||||
<li
|
||||
className={classNames(
|
||||
styles['page-item'],
|
||||
'page-item',
|
||||
{
|
||||
[styles.disabled]: isLastPage,
|
||||
disabled: isLastPage,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
className={['next', styles['page-link']]}
|
||||
className={['next', 'page-link']}
|
||||
aria-label={ariaLabel}
|
||||
label={
|
||||
<div>
|
||||
|
|
@ -271,9 +268,9 @@ class Pagination extends React.Component {
|
|||
<Icon
|
||||
id={newId('pagination-')}
|
||||
className={[
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-chevron-right'],
|
||||
styles['ml-2'],
|
||||
'fa',
|
||||
'fa-chevron-right',
|
||||
'ml-2',
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -292,7 +289,7 @@ class Pagination extends React.Component {
|
|||
const { buttonLabels, pageCount } = this.props;
|
||||
return (
|
||||
<div
|
||||
className={styles['sr-only']}
|
||||
className="sr-only"
|
||||
aria-live="polite"
|
||||
aria-relevant="text"
|
||||
aria-atomic
|
||||
|
|
@ -368,7 +365,7 @@ class Pagination extends React.Component {
|
|||
className={this.props.className}
|
||||
>
|
||||
{this.renderScreenReaderSection()}
|
||||
<ul className={styles.pagination}>
|
||||
<ul className="pagination">
|
||||
{this.renderPreviousButton()}
|
||||
<ExtraSmall>
|
||||
{this.renderPageOfCountButton()}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,12 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies, no-console */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { setConsoleOptions } from '@storybook/addon-console';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import README from './README.md';
|
||||
|
||||
import RadioButtonGroup, { RadioButton } from './index';
|
||||
|
||||
setConsoleOptions({
|
||||
panelExclude: ['warn', 'error'],
|
||||
});
|
||||
|
||||
const onChange = (event) => {
|
||||
console.log(`onChange fired for ${event.target.value}`);
|
||||
|
||||
|
|
@ -44,10 +35,7 @@ const onKeyDown = (event) => {
|
|||
};
|
||||
|
||||
storiesOf('RadioButtonGroup', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('unselected minimal usage', () => (
|
||||
<RadioButtonGroup
|
||||
name="rbg"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ElementPropTypes from 'react-element-proptypes';
|
||||
|
||||
class RadioButton extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
|
@ -169,7 +168,7 @@ RadioButtonGroup.defaultProps = {
|
|||
};
|
||||
|
||||
RadioButtonGroup.propTypes = {
|
||||
children: PropTypes.arrayOf(ElementPropTypes.elementOfType(RadioButton)).isRequired,
|
||||
children: PropTypes.arrayOf(PropTypes.element).isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onBlur: PropTypes.func,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
@import "~bootstrap/scss/_variables";
|
||||
@import "~bootstrap/scss/utilities/_borders";
|
||||
@import "~bootstrap/scss/utilities/_spacing";
|
||||
@import "~bootstrap/scss/_buttons";
|
||||
|
||||
$search-field-clear-btn-width: 30px;
|
||||
$search-field-border-radius: 30px;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,12 @@
|
|||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import SearchField from './index';
|
||||
import README from './README.md';
|
||||
|
||||
storiesOf('SearchField', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<SearchField
|
||||
onSubmit={action('search-submitted')}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
|
||||
import newId from '../utils/newId';
|
||||
import Icon from '../Icon';
|
||||
import InputText from '../InputText';
|
||||
import Button from '../Button';
|
||||
|
||||
import styles from './SearchField.scss';
|
||||
|
||||
class SearchField extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -46,10 +44,10 @@ class SearchField extends React.Component {
|
|||
const buttons = [
|
||||
<Button
|
||||
className={[classNames(
|
||||
styles['search-btn'],
|
||||
'search-btn',
|
||||
{
|
||||
[styles['border-left']]: !isFocused && this.shouldRenderClearButton(),
|
||||
[styles['btn-outline-primary']]: isFocused && this.shouldRenderClearButton(),
|
||||
'border-left': !isFocused && this.shouldRenderClearButton(),
|
||||
'btn-outline-primary': isFocused && this.shouldRenderClearButton(),
|
||||
},
|
||||
)]}
|
||||
label={
|
||||
|
|
@ -65,15 +63,15 @@ class SearchField extends React.Component {
|
|||
buttons.unshift((
|
||||
<Button
|
||||
className={[classNames(
|
||||
styles['clear-btn'],
|
||||
styles['ml-1'],
|
||||
'clear-btn',
|
||||
'ml-1',
|
||||
)]}
|
||||
label={(
|
||||
<small>
|
||||
<Icon
|
||||
className={[classNames(
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-times'],
|
||||
'fa',
|
||||
'fa-times',
|
||||
)]}
|
||||
id={newId('icon-SearchField')}
|
||||
screenReaderText={screenReaderText.clearButton}
|
||||
|
|
@ -139,10 +137,10 @@ class SearchField extends React.Component {
|
|||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
styles.border,
|
||||
styles['search-field'],
|
||||
'border',
|
||||
'search-field',
|
||||
{
|
||||
[styles.focused]: isFocused,
|
||||
focused: isFocused,
|
||||
},
|
||||
)}
|
||||
onFocus={this.handleFocus}
|
||||
|
|
@ -150,9 +148,9 @@ class SearchField extends React.Component {
|
|||
>
|
||||
<InputText
|
||||
className={[classNames(
|
||||
styles.input,
|
||||
'input',
|
||||
{
|
||||
[styles['no-clear-btn']]: !this.shouldRenderClearButton(),
|
||||
'no-clear-btn': !this.shouldRenderClearButton(),
|
||||
},
|
||||
)]}
|
||||
name="search"
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
@import "~bootstrap/scss/_alert";
|
||||
@import "~bootstrap/scss/_buttons";
|
||||
@import "~bootstrap/scss/_close";
|
||||
@import "~bootstrap/scss/_transitions.scss";
|
||||
|
|
@ -1,12 +1,6 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* eslint-disable no-console */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import PropTypes from 'prop-types';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import StatusAlert from './index';
|
||||
import Button from '../Button';
|
||||
|
|
@ -63,10 +57,7 @@ StatusAlertWrapper.defaultProps = {
|
|||
};
|
||||
|
||||
storiesOf('StatusAlert', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<StatusAlert
|
||||
dialog="You have a status alert!"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import classNames from 'classnames';
|
|||
import PropTypes from 'prop-types';
|
||||
import isRequiredIf from 'react-proptype-conditional-require';
|
||||
|
||||
import styles from './StatusAlert.scss';
|
||||
import Button from '../Button';
|
||||
|
||||
class StatusAlert extends React.Component {
|
||||
|
|
@ -85,14 +84,14 @@ class StatusAlert extends React.Component {
|
|||
<div
|
||||
className={classNames([
|
||||
...className,
|
||||
styles.alert,
|
||||
styles.fade,
|
||||
'alert',
|
||||
'fade',
|
||||
], {
|
||||
[styles['alert-dismissible']]: dismissible,
|
||||
'alert-dismissible': dismissible,
|
||||
}, {
|
||||
[styles[`alert-${alertType}`]]: alertType !== undefined,
|
||||
[`alert-${alertType}`]: alertType !== undefined,
|
||||
}, {
|
||||
[styles.show]: this.state.open,
|
||||
show: this.state.open,
|
||||
})}
|
||||
role="alert"
|
||||
hidden={!this.state.open}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
@import "~bootstrap/scss/_tables";
|
||||
@import "~bootstrap/scss/utilities/_screenreaders.scss";
|
||||
@import "~bootstrap/scss/utilities/_spacing.scss";
|
||||
|
||||
.btn-header {
|
||||
@extend .p-0;
|
||||
padding: 0;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import Table from './index';
|
||||
import README from './README.md';
|
||||
|
|
@ -78,10 +74,7 @@ const sort = function sort(firstElement, secondElement, key, direction) {
|
|||
};
|
||||
|
||||
storiesOf('Table', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('unstyled', () => (
|
||||
<Table
|
||||
data={catData}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { shallow, mount } from 'enzyme';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
import isRequiredIf from 'react-proptype-conditional-require';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './Table.scss';
|
||||
import Button from '../Button';
|
||||
|
||||
class Table extends React.Component {
|
||||
|
|
@ -57,7 +55,7 @@ class Table extends React.Component {
|
|||
const sortIconClassName = ['fa-sort', sortDirection].filter(n => n).join('-');
|
||||
|
||||
return (<span
|
||||
className={classNames(FontAwesomeStyles.fa, FontAwesomeStyles[sortIconClassName])}
|
||||
className={classNames('fa', sortIconClassName)}
|
||||
aria-hidden
|
||||
/>);
|
||||
}
|
||||
|
|
@ -66,11 +64,11 @@ class Table extends React.Component {
|
|||
let heading;
|
||||
if (this.props.tableSortable && column.columnSortable) {
|
||||
heading = (<Button
|
||||
className={[styles['btn-header']]}
|
||||
className={['btn-header']}
|
||||
label={
|
||||
<span>
|
||||
{column.label}
|
||||
<span className={classNames(styles['sr-only'])}>
|
||||
<span className={classNames('sr-only')}>
|
||||
{' '}
|
||||
{this.getSortButtonScreenReaderText(column.key)}
|
||||
</span>
|
||||
|
|
@ -80,7 +78,7 @@ class Table extends React.Component {
|
|||
onClick={() => this.onSortClick(column.key)}
|
||||
/>);
|
||||
} else if (column.hideHeader) {
|
||||
heading = (<span className={classNames(styles['sr-only'])}>{column.label}</span>);
|
||||
heading = (<span className={classNames('sr-only')}>{column.label}</span>);
|
||||
} else {
|
||||
heading = column.label;
|
||||
}
|
||||
|
|
@ -92,7 +90,7 @@ class Table extends React.Component {
|
|||
return (
|
||||
<thead
|
||||
className={classNames(
|
||||
...this.props.headingClassName.map(className => styles[className]),
|
||||
...this.props.headingClassName,
|
||||
{ 'd-inline': this.props.hasFixedColumnWidths },
|
||||
)}
|
||||
>
|
||||
|
|
@ -118,6 +116,7 @@ class Table extends React.Component {
|
|||
return (
|
||||
<tbody className={classNames({ 'd-inline': this.props.hasFixedColumnWidths })}>
|
||||
{this.props.data.map((row, i) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<tr key={i} className={classNames({ 'd-flex': this.props.hasFixedColumnWidths })}>
|
||||
{this.props.columns.map(({ key, width }) => (
|
||||
React.createElement(
|
||||
|
|
@ -139,8 +138,8 @@ class Table extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<table className={classNames(
|
||||
styles.table,
|
||||
...this.props.className.map(className => styles[className]),
|
||||
'table',
|
||||
...this.props.className,
|
||||
)}
|
||||
>
|
||||
{this.getCaption()}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
@import "~bootstrap/scss/_nav";
|
||||
|
|
@ -1,19 +1,11 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import Tabs from './index';
|
||||
import README from './README.md';
|
||||
|
||||
storiesOf('Tabs', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('basic usage', () => (
|
||||
<Tabs
|
||||
labels={[
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import Tabs from './index';
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import classNames from 'classnames';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import Button from '../Button';
|
||||
import styles from './Tabs.scss';
|
||||
import newId from '../utils/newId';
|
||||
|
||||
class Tabs extends React.Component {
|
||||
|
|
@ -52,9 +51,9 @@ class Tabs extends React.Component {
|
|||
key={labelId}
|
||||
onClick={() => { this.toggle(i); }}
|
||||
className={classNames(
|
||||
styles['nav-link'],
|
||||
styles['nav-item'],
|
||||
{ [styles.active]: selected },
|
||||
'nav-link',
|
||||
'nav-item',
|
||||
{ active: selected },
|
||||
).split(' ')}
|
||||
label={label}
|
||||
/>
|
||||
|
|
@ -72,8 +71,8 @@ class Tabs extends React.Component {
|
|||
aria-hidden={!selected}
|
||||
aria-labelledby={this.genLabelId(i)}
|
||||
className={classNames(
|
||||
styles['tab-pane'],
|
||||
{ [styles.active]: selected },
|
||||
'tab-pane',
|
||||
{ active: selected },
|
||||
)}
|
||||
id={panelId}
|
||||
key={panelId}
|
||||
|
|
@ -94,13 +93,13 @@ class Tabs extends React.Component {
|
|||
<div
|
||||
role="tablist"
|
||||
className={classNames([
|
||||
styles.nav,
|
||||
styles['nav-tabs'],
|
||||
'nav',
|
||||
'nav-tabs',
|
||||
])}
|
||||
>
|
||||
{labels}
|
||||
</div>
|
||||
<div role="tabpanel" className={styles['tab-content']}>
|
||||
<div role="tabpanel" className="tab-content">
|
||||
{panels}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,20 +1,12 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { checkA11y } from '@storybook/addon-a11y';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withReadme } from 'storybook-readme';
|
||||
|
||||
import TextArea from './index';
|
||||
|
||||
import README from './README.md';
|
||||
|
||||
storiesOf('Textarea', module)
|
||||
.addDecorator((story, context) => withInfo()(story)(context))
|
||||
.addDecorator(centered)
|
||||
.addDecorator(checkA11y)
|
||||
.addDecorator(withReadme(README))
|
||||
.addParameters({ info: { text: README } })
|
||||
.add('minimal usage', () => (
|
||||
<TextArea
|
||||
name="name"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
@import "~bootstrap/scss/_forms";
|
||||
@import "~bootstrap/scss/utilities/_screenreaders.scss";
|
||||
|
||||
.form-control.is-invalid ~ .invalid-feedback-nodanger {
|
||||
color: $body-color;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import FontAwesomeStyles from 'font-awesome/css/font-awesome.min.css';
|
||||
|
||||
import styles from './ValidationMessage.scss';
|
||||
import Variant from '../utils/constants';
|
||||
|
||||
const inputProps = {
|
||||
|
|
@ -35,7 +33,7 @@ class ValidationMessage extends React.Component {
|
|||
|
||||
switch (variant.status) {
|
||||
case Variant.status.INFO:
|
||||
className = styles['invalid-feedback-nodanger'];
|
||||
className = 'invalid-feedback-nodanger';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -57,14 +55,14 @@ class ValidationMessage extends React.Component {
|
|||
<React.Fragment>
|
||||
<span
|
||||
className={classNames(
|
||||
FontAwesomeStyles.fa,
|
||||
FontAwesomeStyles['fa-exclamation-circle'],
|
||||
styles['fa-icon-spacing'],
|
||||
'fa',
|
||||
'fa-exclamation-circle',
|
||||
'fa-icon-spacing',
|
||||
)}
|
||||
aria-hidden
|
||||
/>
|
||||
<span
|
||||
className={classNames(styles['sr-only'])}
|
||||
className={classNames('sr-only')}
|
||||
>
|
||||
{variantIconDescription}
|
||||
</span>
|
||||
|
|
@ -88,7 +86,7 @@ class ValidationMessage extends React.Component {
|
|||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
styles['invalid-feedback'],
|
||||
'invalid-feedback',
|
||||
this.getVariantFeedbackClassName(),
|
||||
className,
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
@import "~bootstrap/scss/_forms";
|
||||
@import "~bootstrap/scss/mixins/_forms";
|
||||
@import "~bootstrap/scss/utilities/_screenreaders.scss";
|
||||
@import '~bootstrap/scss/input-group';
|
||||
|
||||
.form-control.is-invalid.is-invalid-nodanger {
|
||||
border-color: $input-border-color;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import newId from '../utils/newId';
|
||||
import styles from './asInput.scss';
|
||||
import ValidationMessage from '../ValidationMessage/index';
|
||||
import Variant from '../utils/constants';
|
||||
|
||||
|
|
@ -129,7 +128,7 @@ const asInput = (WrappedComponent, inputType = undefined, labelFirst = true) =>
|
|||
|
||||
if (this.props.description) {
|
||||
desc.description = (
|
||||
<small className={styles['form-text']} id={descriptionId} key="1">
|
||||
<small className="form-text" id={descriptionId} key="1">
|
||||
{this.props.description}
|
||||
</small>
|
||||
);
|
||||
|
|
@ -157,7 +156,7 @@ const asInput = (WrappedComponent, inputType = undefined, labelFirst = true) =>
|
|||
id={`label-${this.state.id}`}
|
||||
htmlFor={this.state.id}
|
||||
className={classNames({
|
||||
[styles['form-check-label']]: this.isGroupedInput(),
|
||||
'form-check-label': this.isGroupedInput(),
|
||||
})}
|
||||
>
|
||||
{this.props.label}
|
||||
|
|
@ -212,10 +211,10 @@ const asInput = (WrappedComponent, inputType = undefined, labelFirst = true) =>
|
|||
{...this.state}
|
||||
className={[classNames(
|
||||
{
|
||||
[styles['form-control']]: !this.isGroupedInput(),
|
||||
[styles['form-check-input']]: this.isGroupedInput(),
|
||||
[styles['is-invalid']]: !this.state.isValid,
|
||||
[styles['is-invalid-nodanger']]: !this.hasDangerTheme(),
|
||||
'form-control': !this.isGroupedInput(),
|
||||
'form-check-input': this.isGroupedInput(),
|
||||
'is-invalid': !this.state.isValid,
|
||||
'is-invalid-nodanger': !this.hasDangerTheme(),
|
||||
},
|
||||
className,
|
||||
).trim()]}
|
||||
|
|
@ -229,7 +228,7 @@ const asInput = (WrappedComponent, inputType = undefined, labelFirst = true) =>
|
|||
|
||||
renderInputGroupAppend() {
|
||||
return (
|
||||
<div className={styles['input-group-append']}>
|
||||
<div className="input-group-append">
|
||||
{this.getAddons({ type: 'append', addonElements: this.props.inputGroupAppend })}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -237,7 +236,7 @@ const asInput = (WrappedComponent, inputType = undefined, labelFirst = true) =>
|
|||
|
||||
renderInputGroupPrepend() {
|
||||
return (
|
||||
<div className={styles['input-group-prepend']}>
|
||||
<div className="input-group-prepend">
|
||||
{this.getAddons({ type: 'prepend', addonElements: this.props.inputGroupPrepend })}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -247,14 +246,14 @@ const asInput = (WrappedComponent, inputType = undefined, labelFirst = true) =>
|
|||
const { description, error, describedBy } = this.getDescriptions();
|
||||
return (
|
||||
<div className={classNames({
|
||||
[styles['form-group']]: !this.isGroupedInput(),
|
||||
[styles['form-inline']]: !this.isGroupedInput() && this.props.inline,
|
||||
[styles['form-check']]: this.isGroupedInput(),
|
||||
'form-group': !this.isGroupedInput(),
|
||||
'form-inline': !this.isGroupedInput() && this.props.inline,
|
||||
'form-check': this.isGroupedInput(),
|
||||
})}
|
||||
>
|
||||
{labelFirst && this.getLabel()}
|
||||
{this.props.inputGroupPrepend || this.props.inputGroupAppend ? (
|
||||
<div className={classNames(styles['input-group'])}>
|
||||
<div className={classNames('input-group')}>
|
||||
{this.renderInputGroupPrepend()}
|
||||
{this.renderInput(describedBy)}
|
||||
{this.renderInputGroupAppend()}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
@import './asInput/asInput.scss';
|
||||
@import './Collapsible/Collapsible.scss';
|
||||
@import './Dropdown/Dropdown.scss';
|
||||
@import './Fieldset/Fieldset.scss';
|
||||
@import './Modal/Modal.scss';
|
||||
@import './Pagination/Pagination.scss';
|
||||
@import './SearchField/SearchField.scss';
|
||||
@import './Table/Table.scss';
|
||||
@import './ValidationMessage/ValidationMessage.scss';
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
// TODO: @jaebradley refactor this when Jest issue (https://github.com/facebook/jest/issues/4545#issuecomment-333004504) is patched
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import raf from './tempPolyfills'; // need to import this first to ignore warnings
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies, import/first
|
||||
import Enzyme from 'enzyme';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies, import/first
|
||||
import Adapter from 'enzyme-adapter-react-16';
|
||||
import registerRequireContextHook from 'babel-plugin-require-context-hook/register';
|
||||
|
||||
// Polyfill Webpack's require.context function for our test environment.
|
||||
// It is currently used in .storybook/config.js. See this for more info:
|
||||
// https://www.npmjs.com/package/@storybook/addon-storyshots#configure-jest-to-work-with-webpacks-requirecontext
|
||||
registerRequireContextHook();
|
||||
|
||||
|
||||
Enzyme.configure({ adapter: new Adapter() });
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
.paragon-component {
|
||||
@import "~bootstrap/scss/_reboot";
|
||||
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
box-sizing: border-box;
|
||||
line-height: 1.15;
|
||||
|
||||
font-size: $font-size-base;
|
||||
font-weight: $font-weight-base;
|
||||
line-height: $line-height-base;
|
||||
margin: 0;
|
||||
color: $body-color;
|
||||
background-color: $body-bg;
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@import "~bootstrap/scss/functions";
|
||||
@import "~bootstrap/scss/variables";
|
||||
@import "~bootstrap/scss/mixins";
|
||||
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
@import '~@edx/edx-bootstrap/sass/open-edx/theme';
|
||||
@import "~bootstrap/scss/variables";
|
||||
@import "~bootstrap/scss/mixins";
|
||||
|
||||
// Variable Overrides
|
||||
|
||||
$font-family-base: inherit;
|
||||
$font-family-sans-serif: inherit;
|
||||
$font-family-serif: inherit;
|
||||
|
||||
// Because Bootstrap styles are based on rems, the root HTML element's
|
||||
// font size determines the size of the components. Because the consumer
|
||||
// may want to render Paragon components alongside existing UI, we provide
|
||||
// the option to scale all rem-based variables within Paragon based on
|
||||
// their own base rem value.
|
||||
|
||||
// It's worth looking into doing this on-the-fly via postcss or something
|
||||
// similar, so we don't have to hardcode the overrides.
|
||||
|
||||
// (font size on root element / default (16px))
|
||||
$base-rem-size: 1 !default;
|
||||
|
||||
$spacer: $spacer / $base-rem-size;
|
||||
$font-size-base: $font-size-base / $base-rem-size;
|
||||
$font-size-lg: $font-size-lg / $base-rem-size;
|
||||
$font-size-sm: $font-size-sm / $base-rem-size;
|
||||
$h1-font-size: $h1-font-size / $base-rem-size;
|
||||
$h2-font-size: $h2-font-size / $base-rem-size;
|
||||
$h3-font-size: $h3-font-size / $base-rem-size;
|
||||
$h4-font-size: $h4-font-size / $base-rem-size;
|
||||
$h5-font-size: $h5-font-size / $base-rem-size;
|
||||
$h6-font-size: $h6-font-size / $base-rem-size;
|
||||
$display1-size: $display1-size / $base-rem-size;
|
||||
$display2-size: $display2-size / $base-rem-size;
|
||||
$display3-size: $display3-size / $base-rem-size;
|
||||
$display4-size: $display4-size / $base-rem-size;
|
||||
$lead-font-size: $lead-font-size / $base-rem-size;
|
||||
$kbd-box-shadow: $kbd-box-shadow / $base-rem-size;
|
||||
$border-radius: $border-radius / $base-rem-size;
|
||||
$border-radius-lg: $border-radius-lg / $base-rem-size;
|
||||
$border-radius-sm: $border-radius-sm / $base-rem-size;
|
||||
$table-cell-padding: $table-cell-padding / $base-rem-size;
|
||||
$table-cell-padding-sm: $table-cell-padding-sm / $base-rem-size;
|
||||
$btn-block-spacing-y: $btn-block-spacing-y / $base-rem-size;
|
||||
$input-btn-padding-x: $input-btn-padding-x / $base-rem-size;
|
||||
$input-btn-padding-y: $input-btn-padding-y / $base-rem-size;
|
||||
$input-btn-padding-x-sm: $input-btn-padding-x-sm / $base-rem-size;
|
||||
$input-btn-padding-y-sm: $input-btn-padding-y-sm / $base-rem-size;
|
||||
$input-btn-padding-x-lg: $input-btn-padding-x-lg / $base-rem-size;
|
||||
$input-btn-padding-y-lg: $input-btn-padding-y-lg / $base-rem-size;
|
||||
$form-text-margin-top: $form-text-margin-top / $base-rem-size;
|
||||
$form-check-input-gutter: $form-check-input-gutter / $base-rem-size;
|
||||
$form-check-input-margin-y: $form-check-input-margin-y / $base-rem-size;
|
||||
$form-check-input-margin-x: $form-check-input-margin-x / $base-rem-size;
|
||||
$form-check-inline-margin-x: $form-check-inline-margin-x / $base-rem-size;
|
||||
$custom-control-gutter: $custom-control-gutter / $base-rem-size;
|
||||
$custom-control-spacer-x: $custom-control-spacer-x / $base-rem-size;
|
||||
$custom-control-indicator-size: $custom-control-indicator-size / $base-rem-size;
|
||||
$custom-control-indicator-box-shadow: $custom-control-indicator-box-shadow / $base-rem-size;
|
||||
$custom-select-padding-x: $custom-select-padding-x / $base-rem-size;
|
||||
$custom-select-padding-y: $custom-select-padding-y / $base-rem-size;
|
||||
$custom-select-indicator-padding: $custom-select-indicator-padding / $base-rem-size;
|
||||
$custom-file-height: $custom-file-height / $base-rem-size;
|
||||
$custom-file-focus-box-shadow: $custom-file-focus-box-shadow / $base-rem-size;
|
||||
$custom-file-padding-x: $custom-file-padding-x / $base-rem-size;
|
||||
$custom-file-padding-y: $custom-file-padding-y / $base-rem-size;
|
||||
$custom-file-box-shadow: $custom-file-box-shadow / $base-rem-size;
|
||||
$dropdown-min-width: $dropdown-min-width / $base-rem-size;
|
||||
$dropdown-padding-y: $dropdown-padding-y / $base-rem-size;
|
||||
$dropdown-spacer: $dropdown-spacer / $base-rem-size;
|
||||
$dropdown-box-shadow: $dropdown-box-shadow / $base-rem-size;
|
||||
$dropdown-item-padding-x: $dropdown-item-padding-x / $base-rem-size;
|
||||
$dropdown-item-padding-y: $dropdown-item-padding-y / $base-rem-size;
|
||||
$navbar-brand-padding-y: $navbar-brand-padding-y / $base-rem-size;
|
||||
$navbar-toggler-padding-x: $navbar-toggler-padding-x / $base-rem-size;
|
||||
$navbar-toggler-padding-y: $navbar-toggler-padding-y / $base-rem-size;
|
||||
$pagination-padding-x: $pagination-padding-x / $base-rem-size;
|
||||
$pagination-padding-y: $pagination-padding-y / $base-rem-size;
|
||||
$pagination-padding-x-sm: $pagination-padding-x-sm / $base-rem-size;
|
||||
$pagination-padding-y-sm: $pagination-padding-y-sm / $base-rem-size;
|
||||
$pagination-padding-x-lg: $pagination-padding-x-lg / $base-rem-size;
|
||||
$pagination-padding-y-lg: $pagination-padding-y-lg / $base-rem-size;
|
||||
$jumbotron-padding: $jumbotron-padding / $base-rem-size;
|
||||
$card-spacer-x: $card-spacer-x / $base-rem-size;
|
||||
$card-spacer-y: $card-spacer-y / $base-rem-size;
|
||||
$card-img-overlay-padding: $card-img-overlay-padding / $base-rem-size;
|
||||
$card-columns-gap: $card-columns-gap / $base-rem-size;
|
||||
$badge-pill-border-radius: $badge-pill-border-radius / $base-rem-size;
|
||||
$alert-padding-x: $alert-padding-x / $base-rem-size;
|
||||
$alert-padding-y: $alert-padding-y / $base-rem-size;
|
||||
$progress-height: $progress-height / $base-rem-size;
|
||||
$progress-font-size: $progress-font-size / $base-rem-size;
|
||||
$progress-box-shadow: $progress-box-shadow / $base-rem-size;
|
||||
$list-group-item-padding-x: $list-group-item-padding-x / $base-rem-size;
|
||||
$list-group-item-padding-y: $list-group-item-padding-y / $base-rem-size;
|
||||
$thumbnail-padding: $thumbnail-padding / $base-rem-size;
|
||||
$breadcrumb-padding-y: $breadcrumb-padding-y / $base-rem-size;
|
||||
$breadcrumb-padding-x: $breadcrumb-padding-x / $base-rem-size;
|
||||
$breadcrumb-item-padding: $breadcrumb-item-padding / $base-rem-size;
|
||||
$kbd-padding-x: $kbd-padding-x / $base-rem-size;
|
||||
$kbd-padding-y: $kbd-padding-y / $base-rem-size;
|
||||
|
|
@ -1,112 +1,81 @@
|
|||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||
|
||||
// we build the library for two different build targets:
|
||||
// static (with scoped styles) and themeable (with stock,
|
||||
// overrideable classnames)
|
||||
const targetProperties = [{
|
||||
baseDirectory: 'static',
|
||||
localIdentName: 'paragon__[local]',
|
||||
},
|
||||
{
|
||||
baseDirectory: 'themeable',
|
||||
localIdentName: '[local]',
|
||||
}];
|
||||
|
||||
module.exports = targetProperties.map(config => ({
|
||||
devtool: 'source-map',
|
||||
module.exports = {
|
||||
entry: {
|
||||
paragon: path.resolve('./src/index.js'),
|
||||
paragon: './src/index.js',
|
||||
style: './src/index.scss',
|
||||
},
|
||||
output: {
|
||||
filename: `${config.baseDirectory}/index.js`,
|
||||
filename: '[name].js',
|
||||
library: 'paragon',
|
||||
libraryTarget: 'umd',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx'],
|
||||
},
|
||||
externals: {
|
||||
react: {
|
||||
commonjs: 'react',
|
||||
commonjs2: 'react',
|
||||
amd: 'React',
|
||||
root: 'React',
|
||||
},
|
||||
'react-dom': {
|
||||
commonjs: 'react-dom',
|
||||
commonjs2: 'react-dom',
|
||||
amd: 'ReactDOM',
|
||||
root: 'ReactDOM',
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new UglifyJsPlugin({
|
||||
sourceMap: true,
|
||||
}),
|
||||
new ExtractTextPlugin(`${config.baseDirectory}/paragon.min.css`),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
new UglifyJsPlugin(),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'paragon.min.css',
|
||||
}),
|
||||
// Be careful here. Our output path is the root of this project
|
||||
// so without this config, CleanWebpackPlugin will destroy the project
|
||||
// We should change the output path to dist/ in the next major version.
|
||||
new CleanWebpackPlugin(),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: /(node_modules)/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
{ loader: 'source-map-loader' },
|
||||
],
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.scss|\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
use: [
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
modules: true,
|
||||
localIdentName: config.localIdentName,
|
||||
sourceMap: true,
|
||||
minimize: true,
|
||||
},
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: 'css-loader',
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
data: '@import "bootstrap-variables";',
|
||||
includePaths: [
|
||||
path.join(__dirname, './src/utils'),
|
||||
path.join(__dirname, './node_modules'),
|
||||
],
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
data: '@import "paragon-reset";',
|
||||
includePaths: [
|
||||
path.join(__dirname, './src/utils'),
|
||||
path.join(__dirname, './node_modules'),
|
||||
],
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|ttf|svg|eot)(\?v=\d+\.\d+\.\d+)?$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
outputPath: `${config.baseDirectory}/`,
|
||||
outputPath: 'assets',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
externals: [{
|
||||
react: {
|
||||
root: 'React',
|
||||
commonjs2: 'react',
|
||||
commonjs: 'react',
|
||||
amd: 'react',
|
||||
},
|
||||
},
|
||||
{
|
||||
'react-dom': {
|
||||
root: 'ReactDOM',
|
||||
commonjs2: 'react-dom',
|
||||
commonjs: 'react-dom',
|
||||
amd: 'react-dom',
|
||||
},
|
||||
},
|
||||
{
|
||||
'react-transition-group': {
|
||||
root: 'ReactTransitionGroup',
|
||||
commonjs2: 'react-transition-group',
|
||||
commonjs: 'react-transition-group',
|
||||
amd: 'react-transition-group',
|
||||
},
|
||||
}],
|
||||
}));
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue