diff --git a/.storybook/blocks/DocsContainer.tsx b/.storybook/blocks/DocsContainer.tsx index 7908ba595..f332e4146 100644 --- a/.storybook/blocks/DocsContainer.tsx +++ b/.storybook/blocks/DocsContainer.tsx @@ -16,6 +16,9 @@ import { SourceContainer } from './SourceContainer'; import { CodeOrSourceMdx, AnchorMdx, HeadersMdx } from './mdx'; import { scrollToElement } from './utils'; +import { Subtitle } from './Subtitle'; +import { Title } from './Title'; + export interface DocsContainerProps { context: DocsContextProps; } @@ -89,6 +92,8 @@ export const DocsContainer: FunctionComponent = ({ + + {children} </DocsContent> </DocsWrapper> diff --git a/.storybook/blocks/DocsPage.tsx b/.storybook/blocks/DocsPage.tsx index 88a053ffc..ee00a7dd6 100644 --- a/.storybook/blocks/DocsPage.tsx +++ b/.storybook/blocks/DocsPage.tsx @@ -42,8 +42,8 @@ export const DocsPage: FC = (props) => { return ( <> - <Subtitle /> - <Title /> + {/*<Subtitle /> + <Title />*/} <Description /> <Primary /> <Props story={PRIMARY_STORY} /> diff --git a/.storybook/preview.js b/.storybook/preview.js index eae1f2658..7a6cf04f7 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -10,9 +10,10 @@ import { Props, Stories, types, - DocsContainer, + //DocsContainer, } from '@storybook/addon-docs/blocks'; import { DocsPage } from './blocks/DocsPage'; +import { DocsContainer } from './blocks/DocsContainer'; import { Subtitle } from './blocks/Subtitle'; import React, { FC } from 'react'; diff --git a/src/components/BannerNavigation/BannerNavigationWithContent.js b/src/components/BannerNavigation/BannerNavigationWithContent.js index 0810c369d..69d0a0650 100644 --- a/src/components/BannerNavigation/BannerNavigationWithContent.js +++ b/src/components/BannerNavigation/BannerNavigationWithContent.js @@ -76,7 +76,7 @@ BannerNavigationWithContent.propTypes = { /** * The CSS class name to be placed on the wrapping element. */ - pageWidth: PropTypes.oneOf(pageWidths), + pageWidth: PropTypes.string, /** * Allows to disable the search input */ diff --git a/src/components/Button/Button.stories.js b/src/components/Button/Button.stories.js index 252e20094..03d27e909 100755 --- a/src/components/Button/Button.stories.js +++ b/src/components/Button/Button.stories.js @@ -84,6 +84,7 @@ on the page. `; ButtonKind.story = { + name: 'kind parameter', parameters: { docs: { storyDescription: hello, @@ -109,6 +110,7 @@ always paired with text whenever possible. `; ButtonIcon.story = { + name: 'with icon', parameters: { docs: { storyDescription: helloButtonIcon, @@ -132,6 +134,7 @@ or less. `; Small.story = { + name: 'small Button', parameters: { docs: { storyDescription: helloButtonSmall, diff --git a/src/components/Checkbox/_checkbox.scss b/src/components/Checkbox/_checkbox.scss index 3772fd9fa..27d9a0aba 100755 --- a/src/components/Checkbox/_checkbox.scss +++ b/src/components/Checkbox/_checkbox.scss @@ -52,7 +52,7 @@ height: rem(18px); width: rem(18px); border: $checkbox-border-width solid $ui-05; - border-radius: $button-border-radius; + border-radius: $checkbox-border-radius; background-color: $ui-01; } diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js index 4f9a3b7e5..7a679cd78 100755 --- a/src/components/Footer/Footer.js +++ b/src/components/Footer/Footer.js @@ -191,23 +191,50 @@ const Footer = ({ }; Footer.propTypes = { + /** + The content of the footer containing relevant links + */ children: PropTypes.node, + /** + Additional className which will be added + */ className: PropTypes.string, + /** + Meta content, usually the copyright notice + */ + metaContent: PropTypes.node, + /** + Optional WFP logo for mobile devices, can be used if the Logo should be provided by the CDN + */ + logo: PropTypes.node, + /** + Optional WFP logo for desktop devices, can be used if the Logo should be provided by the CDN + */ + logoExtended: PropTypes.node, + /** + * The width of the `Wrapper` component + */ + pageWidth: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'full']), + /** + The WFP logo + */ + subTitle: PropTypes.node, + /* labelOne: PropTypes.string, linkTextOne: PropTypes.string, linkHrefOne: PropTypes.string, labelTwo: PropTypes.string, linkTextTwo: PropTypes.string, - linkHrefTwo: PropTypes.string, + linkHrefTwo: PropTypes.string,*/ }; -Footer.defaultProps = { +/*Footer.defaultProps = { labelOne: 'Need Help?', linkTextOne: 'Contact', linkHrefOne: '#', labelTwo: 'Second Link', linkTextTwo: 'About', linkHrefTwo: '#', -}; +};*/ export default Footer; diff --git a/src/components/Footer/Footer.stories.js b/src/components/Footer/Footer.stories.js new file mode 100644 index 000000000..9c3098645 --- /dev/null +++ b/src/components/Footer/Footer.stories.js @@ -0,0 +1,68 @@ +import React from 'react'; + +import markdown from './README.mdx'; + +import Footer from '.'; +import Link from '../Link'; + +export default { + title: 'Components/Footer', + component: Footer, + parameters: { + componentSubtitle: 'Component', + mdx: markdown, + }, +}; + +export const Regular = (args) => ( + <Footer {...args}> + <div className="wfp--footer__info"> + <div className="wfp--footer__info__item"> + <p className="wfp--footer__label">A label</p> + <ul className="wfp--footer__list"> + <li> + <Link href="http://www.wfp.org">First Link</Link> + </li> + <li> + <Link href="http://www.wfp.org">Second Link</Link> + </li> + </ul> + </div> + <div className="wfp--footer__info__item"> + <p className="wfp--footer__label">Another label</p> + <ul className="wfp--footer__list"> + <li> + <Link href="http://www.wfp.org">First Link</Link> + </li> + <li> + <Link href="http://www.wfp.org">Second Link</Link> + </li> + </ul> + </div> + </div> + </Footer> +); + +Regular.args = {}; + +export const External = (args) => ( + <Footer + external + metaContent="2019 © World Food Programme" + secondary={ + <div> + Via C. G. Viola 68 Parco dei Medici + <br /> + 00148 Rome, Italy + </div> + }> + <div> + The United Nations World Food Programme - saving lives in emergencies and + changing lives for millions through sustainable development. WFP works in + more than 80 countries around the world, feeding people caught in conflict + and disasters, and laying the foundations for a better future. + <br /> + <Link href="http://www.wfp.org">Custom Links</Link> + </div> + </Footer> +); diff --git a/src/components/Footer/README.md b/src/components/Footer/README.mdx similarity index 99% rename from src/components/Footer/README.md rename to src/components/Footer/README.mdx index 85e216b31..2ec6ee9e3 100644 --- a/src/components/Footer/README.md +++ b/src/components/Footer/README.mdx @@ -1,5 +1,6 @@ Footer is the second most important place where your basic site information and links should live. Users turn often to the footer to look for important information, such as contact details, or links to less prominent sections of the website (privacy policy, terms of use, etc). +<!-- ```js import { Footer } from '@wfp/ui'; ``` @@ -48,3 +49,4 @@ import { Footer } from '@wfp/ui'; | Logo | `Vertical black english Logo from @wfp/icons` | `string` | Footer Logo on Mobile Devices, if not set the default logo from `@wfp/icons` be used. | | Logo | `Extended black english from @wfp/icons` | `string` | Footer on Desktop Devices, if not set the default logo from `@wfp/icons` be used. | | metaContent | `undefined` | `string` `component` | Footer meta text | +--> diff --git a/src/components/Form/_form.scss b/src/components/Form/_form.scss index 8ef65bdf2..1001e9145 100755 --- a/src/components/Form/_form.scss +++ b/src/components/Form/_form.scss @@ -68,14 +68,14 @@ //box-shadow: 0 2px 0px 0px $support-01; ~ .#{$prefix}--form-requirement { - max-height: rem(200px); + //max-height: rem(200px); display: block; } } .#{$prefix}--form-item--invalid { .#{$prefix}--form-requirement { - max-height: rem(200px); + //max-height: rem(200px); display: block; } } @@ -90,7 +90,7 @@ @include typescale('omega'); margin: $spacing-2xs 0 0; max-height: 0; - overflow: hidden; + //overflow: hidden; font-weight: 600; line-height: 1.5; display: none; diff --git a/src/components/Input/Input.js b/src/components/Input/Input.js index 1f915bbd4..d73aaac47 100644 --- a/src/components/Input/Input.js +++ b/src/components/Input/Input.js @@ -28,12 +28,12 @@ const Input = ({ }) => { const inputProps = { id, - onChange: evt => { + onChange: (evt) => { if (!other.disabled && !other.readOnly) { onChange(evt); } }, - onClick: evt => { + onClick: (evt) => { if (!other.disabled && !other.readOnly) { onClick(evt); } @@ -66,7 +66,7 @@ const Input = ({ const error = invalid ? ( <div className="wfp--form-requirement" id={errorId}> - {invalidText} + {invalid.message ? invalid.message : invalidText} </div> ) : null; diff --git a/src/components/NumberInput/NumberInput.stories.js b/src/components/NumberInput/NumberInput.stories.js old mode 100755 new mode 100644 index 43ba7e01e..c1b0c17ee --- a/src/components/NumberInput/NumberInput.stories.js +++ b/src/components/NumberInput/NumberInput.stories.js @@ -1,41 +1,27 @@ import React from 'react'; -import { storiesOf } from '@storybook/react'; +import markdown from './README.mdx'; import { action } from '@storybook/addon-actions'; - -import { withKnobs, boolean, number, text } from '@storybook/addon-knobs'; import NumberInput from '.'; -import NumberInputSkeleton from './NumberInput.Skeleton'; -const props = () => ({ - className: 'some-class', - id: 'tj-input', - addonBefore: text('addonBefore', '€'), - addonAfter: text('addonAfter', '$'), - formItemClassName: text( - 'Form item className (formItemClassName)', - 'some-form-item-class' - ), - labelText: text('Label text (labelText)', 'Number Input label'), - hideControls: boolean('hide controls (hideControls)', false), - hideLabel: boolean('No label (hideLabel)', false), - min: number('Minimum value (min)', 0), - max: number('Maximum value (max)', 100), - value: number('Value (value)', 50), - step: number('Step of up/down arrow (step)', 10), - disabled: boolean('Disabled (disabled)', false), - invalid: boolean('Show form validation UI (invalid)', false), - invalidText: text( - 'Form validation UI content (invalidText)', - 'Number is not valid' - ), - helperText: text('Helper text (helperText)', 'Optional helper text.'), - light: boolean('Light variant (light)', false), - onChange: action('onChange'), - onClick: action('onClick'), - allowEmpty: boolean('Allow empty value (allowEmpty)', false), -}); +export default { + title: 'Components/NumberInput', + component: NumberInput, + parameters: { + componentSubtitle: 'Component', + mdx: markdown, + }, +}; + +export const Regular = (args) => <NumberInput {...args} />; + +const description = ` +You can customize the content by using \`BannerNavigation\`. +`; -storiesOf('Components|NumberInput', module) - .addDecorator(withKnobs) - .add('Default', () => <NumberInput {...props()} />) - .add('skeleton', () => <NumberInputSkeleton />); +Regular.story = { + parameters: { + docs: { + storyDescription: description, + }, + }, +}; diff --git a/src/components/NumberInput/NumberInput.stories.js.legacy b/src/components/NumberInput/NumberInput.stories.js.legacy new file mode 100755 index 000000000..43ba7e01e --- /dev/null +++ b/src/components/NumberInput/NumberInput.stories.js.legacy @@ -0,0 +1,41 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; + +import { withKnobs, boolean, number, text } from '@storybook/addon-knobs'; +import NumberInput from '.'; +import NumberInputSkeleton from './NumberInput.Skeleton'; + +const props = () => ({ + className: 'some-class', + id: 'tj-input', + addonBefore: text('addonBefore', '€'), + addonAfter: text('addonAfter', '$'), + formItemClassName: text( + 'Form item className (formItemClassName)', + 'some-form-item-class' + ), + labelText: text('Label text (labelText)', 'Number Input label'), + hideControls: boolean('hide controls (hideControls)', false), + hideLabel: boolean('No label (hideLabel)', false), + min: number('Minimum value (min)', 0), + max: number('Maximum value (max)', 100), + value: number('Value (value)', 50), + step: number('Step of up/down arrow (step)', 10), + disabled: boolean('Disabled (disabled)', false), + invalid: boolean('Show form validation UI (invalid)', false), + invalidText: text( + 'Form validation UI content (invalidText)', + 'Number is not valid' + ), + helperText: text('Helper text (helperText)', 'Optional helper text.'), + light: boolean('Light variant (light)', false), + onChange: action('onChange'), + onClick: action('onClick'), + allowEmpty: boolean('Allow empty value (allowEmpty)', false), +}); + +storiesOf('Components|NumberInput', module) + .addDecorator(withKnobs) + .add('Default', () => <NumberInput {...props()} />) + .add('skeleton', () => <NumberInputSkeleton />); diff --git a/src/components/NumberInput/README.md b/src/components/NumberInput/README.mdx similarity index 96% rename from src/components/NumberInput/README.md rename to src/components/NumberInput/README.mdx index 58e106887..3ee160c27 100644 --- a/src/components/NumberInput/README.md +++ b/src/components/NumberInput/README.mdx @@ -1,5 +1,6 @@ **NumberInput** are used to let the user enter a number. They include built-in validation to reject non-numerical entries. They provide stepper arrows to let the user increase and decrease the value using their mouse or by simply tapping with a fingertip. +<!-- ### Usage with react ```js import { NumberInput } from '@wfp/ui'; @@ -7,4 +8,5 @@ import { NumberInput } from '@wfp/ui'; ```js <NumberInput name="inputname" /> -``` \ No newline at end of file +``` +--> diff --git a/src/components/Pagination/_pagination.scss b/src/components/Pagination/_pagination.scss index fa9b29453..70078e240 100644 --- a/src/components/Pagination/_pagination.scss +++ b/src/components/Pagination/_pagination.scss @@ -9,7 +9,7 @@ $css--helpers: true; @import '../../globals/scss/css--helpers'; @import '../../globals/scss/import-once'; @import '../Select/select'; -@import '../TextInput/text-input'; +//@import '../TextInput/text-input'; @include exports('pagination') { .#{$prefix}--data-table-v2-container + .#{$prefix}--pagination { diff --git a/src/components/ReactDropzone/_react-dropzone.scss b/src/components/ReactDropzone/_react-dropzone.scss index d384a551a..4d9f2c4c9 100644 --- a/src/components/ReactDropzone/_react-dropzone.scss +++ b/src/components/ReactDropzone/_react-dropzone.scss @@ -9,7 +9,7 @@ $css--helpers: true; @import '../../globals/scss/css--helpers'; @import '../../globals/scss/import-once'; @import '../Select/select'; -@import '../TextInput/text-input'; +//@import '../TextInput/text-input'; @include exports('react-dropzone') { .#{$prefix}--dropzone { diff --git a/src/components/Slider/_slider.scss b/src/components/Slider/_slider.scss index 06fd3d82e..d6e8c3d45 100755 --- a/src/components/Slider/_slider.scss +++ b/src/components/Slider/_slider.scss @@ -11,7 +11,7 @@ @import '../../globals/scss/layout'; @import '../../globals/scss/import-once'; @import '../Form/form'; -@import '../TextInput/text-input'; +//@import '../TextInput/text-input'; @include exports('slider') { .#{$prefix}--slider-container { diff --git a/src/components/Tag/README.md b/src/components/Tag/README.md deleted file mode 100755 index 3f68bd910..000000000 --- a/src/components/Tag/README.md +++ /dev/null @@ -1,32 +0,0 @@ -Tags are used for items that need to be labeled, categorized, or organized using keywords that describe them. - -### Usage with react -```js -import { Tag } from '@wfp/ui'; -``` - -```js -<Tag className="some-class" type="wfp"> - This is a tag -</Tag> -``` - - -### SCSS - -#### Mixins - -Mixins specific to tag are located in [src/components/Tag/_mixins.scss](). - -| Name | Params | Description | -|-----------|------------------------|--------------------------------------------| -| tag-theme | $bg-color, $text-color | Adds given background-color and text colour | - -#### Modifiers - -Use these modifiers with `.wfp--tag` class. - -| Selector | Description | -|------------------------|------------------------------------------------------| -| .wfp--tag--beta | Apply the colours for a beta service tag. | -| .wfp--tag--custom | Apply the colours for a custom tag. | \ No newline at end of file diff --git a/src/components/Tag/README.mdx b/src/components/Tag/README.mdx new file mode 100755 index 000000000..d974ccb1e --- /dev/null +++ b/src/components/Tag/README.mdx @@ -0,0 +1,33 @@ +Tags are used for items that need to be labeled, categorized, or organized using keywords that describe them. + +<!-- +### Usage with react +```js +import { Tag } from '@wfp/ui'; +``` + +```js +<Tag className="some-class" type="wfp"> + This is a tag +</Tag> +``` +--> + +### SCSS + +#### Mixins + +Mixins specific to tag are located in [src/components/Tag/\_mixins.scss](). + +| Name | Params | Description | +| --------- | ---------------------- | ------------------------------------------- | +| tag-theme | $bg-color, $text-color | Adds given background-color and text colour | + +#### Modifiers + +Use these modifiers with `.wfp--tag` class. + +| Selector | Description | +| ----------------- | ----------------------------------------- | +| .wfp--tag--beta | Apply the colours for a beta service tag. | +| .wfp--tag--custom | Apply the colours for a custom tag. | diff --git a/src/components/Tag/Tag.js b/src/components/Tag/Tag.js index cea85e4b7..8d50abdbf 100755 --- a/src/components/Tag/Tag.js +++ b/src/components/Tag/Tag.js @@ -38,5 +38,9 @@ Tag.propTypes = { type: PropTypes.oneOf(Object.keys(TYPES)).isRequired, }; +Tag.defaultProps = { + type: 'wfp', +}; + export const types = Object.keys(TYPES); export default Tag; diff --git a/src/components/Tag/Tag.stories.js b/src/components/Tag/Tag.stories.js new file mode 100644 index 000000000..7e5270da0 --- /dev/null +++ b/src/components/Tag/Tag.stories.js @@ -0,0 +1,27 @@ +import React from 'react'; +import markdown from './README.mdx'; +import { action } from '@storybook/addon-actions'; +import Tag from '.'; + +export default { + title: 'Components/Tag', + component: Tag, + parameters: { + componentSubtitle: 'Component', + mdx: markdown, + }, +}; + +export const Regular = (args) => <Tag {...args}>Text</Tag>; + +const description = ` +You can customize the content by using \`BannerNavigation\`. +`; + +Regular.story = { + parameters: { + docs: { + storyDescription: description, + }, + }, +}; diff --git a/src/components/TextArea/README.md b/src/components/TextArea/README.mdx similarity index 98% rename from src/components/TextArea/README.md rename to src/components/TextArea/README.mdx index c0c582726..0c202fb93 100644 --- a/src/components/TextArea/README.md +++ b/src/components/TextArea/README.mdx @@ -2,6 +2,7 @@ [Carbon Design System Usage for text inputs](https://next.carbondesignsystem.com/components/text-input) +<!-- ### Usage with react ```js import { TextArea } from '@wfp/ui'; @@ -22,4 +23,5 @@ import { TextArea } from '@wfp/ui'; onClick={onClick} onChange={onChange} /> -``` \ No newline at end of file +``` +--> diff --git a/src/components/TextArea/TextArea.stories.js b/src/components/TextArea/TextArea.stories.js new file mode 100644 index 000000000..111ce3984 --- /dev/null +++ b/src/components/TextArea/TextArea.stories.js @@ -0,0 +1,27 @@ +import React from 'react'; +import markdown from './README.mdx'; +import { action } from '@storybook/addon-actions'; +import TextArea from '.'; + +export default { + title: 'Components/TextArea', + component: TextArea, + parameters: { + componentSubtitle: 'Component', + mdx: markdown, + }, +}; + +export const Regular = (args) => <TextArea {...args} />; + +const description = ` +You can customize the content by using \`BannerNavigation\`. +`; + +Regular.story = { + parameters: { + docs: { + storyDescription: description, + }, + }, +}; diff --git a/src/components/TextInput/README.md b/src/components/TextInput/README.mdx similarity index 96% rename from src/components/TextInput/README.md rename to src/components/TextInput/README.mdx index a69d41941..ca20267d4 100644 --- a/src/components/TextInput/README.md +++ b/src/components/TextInput/README.mdx @@ -2,13 +2,14 @@ [Carbon Design System Usage for text inputs](https://next.carbondesignsystem.com/components/text-input) +<!-- ### Usage with react ```js import { TextInput } from '@wfp/ui'; ``` ```js -<TextInput +<TextInput className="some-class" id="test2" defaultValue="Default value (defaultValue)" @@ -21,4 +22,5 @@ import { TextInput } from '@wfp/ui'; helperText="Optional helper text." onClick={onClick} onChange={onChange} -/> \ No newline at end of file +/> +--> diff --git a/src/components/TextInput/TextInput.js b/src/components/TextInput/TextInput.js index 34f606930..4bd60d6cc 100755 --- a/src/components/TextInput/TextInput.js +++ b/src/components/TextInput/TextInput.js @@ -3,101 +3,60 @@ import React from 'react'; import classNames from 'classnames'; import settings from '../../globals/js/settings'; import FormItem from '../FormItem'; +import Input from '../Input/Input'; +import styles from './text-input.module.scss'; const { prefix } = settings; -const TextInput = ({ - additional, - labelText, - className, - id, - formItemClassName, - placeholder, - type, - onChange, - onClick, - hideLabel, - invalid, - invalidText, - helperText, - inputRef, - light, - ...other -}) => { - const textInputProps = { +const TextInput = (props) => { + const { + additional, + disabled, + labelText, + className, id, - onChange: evt => { - if (!other.disabled && !other.readOnly) { - onChange(evt); - } - }, - onClick: evt => { - if (!other.disabled && !other.readOnly) { - onClick(evt); - } - }, + formItemClassName, placeholder, type, - }; - - const errorId = id + '-error-msg'; - const textInputClasses = classNames(`${prefix}--text-input`, className, { - [`${prefix}--text-input--light`]: light, - [`${prefix}--text-input--invalid`]: invalid, - }); - - const labelClasses = classNames(`${prefix}--label`, { - [`${prefix}--visually-hidden`]: hideLabel, - [`${prefix}--label--disabled`]: other.disabled, + onChange, + onClick, + hideLabel, + invalid, + invalidText, + helperText, + inputRef, + light, + pattern, + ...other + } = props; + + const textInputClasses = classNames(`${prefix}--text`, className, { + [`${prefix}--text--light`]: light, + [`${prefix}--text--helpertext`]: helperText, + [`${prefix}--text--nolabel`]: hideLabel, + [styles.text]: true, }); - const helperTextClasses = classNames(`${prefix}--form__helper-text`, { - [`${prefix}--form__helper-text--disabled`]: other.disabled, - }); - - const label = labelText ? ( - <label htmlFor={id} className={labelClasses}> - {labelText} - </label> - ) : null; - - const error = invalid ? ( - <div className="wfp--form-requirement" id={errorId}> - {invalidText} - </div> - ) : null; - - const input = invalid ? ( - <input - {...other} - {...textInputProps} - ref={inputRef} - data-invalid - aria-invalid - aria-describedby={errorId} - className={textInputClasses} - /> - ) : ( - <input - {...other} - {...textInputProps} - className={textInputClasses} - ref={inputRef} - /> - ); - - const helper = helperText ? ( - <div className={helperTextClasses}>{helperText}</div> - ) : null; + const newProps = { + disabled, + id, + }; return ( - <FormItem className={formItemClassName}> - {label} - {helper} - {additional} - {input} - {error} - </FormItem> + <Input {...props} formItemClassName={textInputClasses}> + {(e) => { + return ( + <input + pattern={pattern} + {...other} + {...newProps} + ref={inputRef} + className={textInputClasses} + {...e} + /> + ); + }} + </Input> ); }; @@ -125,13 +84,18 @@ TextInput.propTypes = { /** * Specify a custom `id` for the <input> */ - id: PropTypes.string.isRequired, + id: PropTypes.string, /** * Provide the text that will be read by a screen reader when visiting this * control */ - labelText: PropTypes.node.isRequired, + labelText: PropTypes.node, + + /** + * Specify a custom `name` for the <input> + */ + name: PropTypes.string.isRequired, /** * Optionally provide an `onChange` handler that is called whenever <input> diff --git a/src/components/TextInput/TextInput.stories.js b/src/components/TextInput/TextInput.stories.js new file mode 100644 index 000000000..89bf4ee07 --- /dev/null +++ b/src/components/TextInput/TextInput.stories.js @@ -0,0 +1,42 @@ +import React from 'react'; +import markdown from './README.mdx'; +import { action } from '@storybook/addon-actions'; +import TextInput from '.'; + +export default { + title: 'Components/TextInput', + component: TextInput, + parameters: { + componentSubtitle: 'Component', + mdx: markdown, + }, +}; + +export const Regular = (args) => <TextInput {...args} />; + +const description = ` +You can customize the content by using \`BannerNavigation\`. +`; + +Regular.args = { + name: 'inputname', + helperText: 'Optional helperText', + labelText: 'The labelText', +}; + +export const withError = (args) => <TextInput {...args} />; + +withError.story = { + parameters: { + docs: { + storyDescription: description, + }, + }, +}; + +withError.args = { + name: 'inputname', + helperText: 'Optional helperText', + labelText: 'The labelText', + invalid: { message: 'Please enter your first name' }, +}; diff --git a/src/components/TextInput/_text-input.scss b/src/components/TextInput/text-input.module.scss similarity index 88% rename from src/components/TextInput/_text-input.scss rename to src/components/TextInput/text-input.module.scss index f704eff42..67c88145a 100755 --- a/src/components/TextInput/_text-input.scss +++ b/src/components/TextInput/text-input.module.scss @@ -12,11 +12,10 @@ @import '../Form/form'; @include exports('text-input') { - .#{$prefix}--text-input { - @include reset; - @include input-base; - @include font-family; + .text input { @include typescale('zeta'); - width: 100%; + @include font-family; + @include input-base; + @include input-error; } } diff --git a/src/components/Toggle/README.md b/src/components/Toggle/README.mdx similarity index 91% rename from src/components/Toggle/README.md rename to src/components/Toggle/README.mdx index 2e31e832d..1b80d242e 100644 --- a/src/components/Toggle/README.md +++ b/src/components/Toggle/README.mdx @@ -3,6 +3,7 @@ an uncontrolled Toggle component. To use the Toggle component as a controlled co Setting the toggled property will allow you to change the value dynamically, whereas setting the defaultToggled prop will only set the value initially. +<!-- ### Usage with react ```js import { Toggle } from '@wfp/ui'; @@ -15,7 +16,8 @@ import { Toggle } from '@wfp/ui'; labelB="On" onChange={onChange} onToggle={onToggle} - className="some-class" - id="toggle-1" + className="some-class" + id="toggle-1" /> -``` \ No newline at end of file +``` +--> diff --git a/src/components/Toggle/Toggle.js b/src/components/Toggle/Toggle.js index 5e2239be3..517c3e42f 100755 --- a/src/components/Toggle/Toggle.js +++ b/src/components/Toggle/Toggle.js @@ -9,6 +9,7 @@ const Toggle = ({ onChange, onToggle, id, + name, labelA, labelB, ...other @@ -27,24 +28,27 @@ const Toggle = ({ checkedProps.defaultChecked = defaultToggled; } + const htmlFor = id ? id : name; + return ( <div className={wrapperClasses}> <input {...other} {...checkedProps} type="checkbox" - id={id} + id={htmlFor} className="wfp--toggle" - onChange={evt => { + onChange={(evt) => { + console.log('change', evt); onChange && onChange(evt); - onToggle(input.checked, id, evt); + onToggle(input.checked, htmlFor, evt); }} - ref={el => { + ref={(el) => { input = el; }} /> - <label className="wfp--toggle__label" htmlFor={id}> + <label className="wfp--toggle__label" htmlFor={htmlFor}> <span className="wfp--toggle__text--left">{labelA}</span> <span className="wfp--toggle__appearance" /> <span className="wfp--toggle__text--right">{labelB}</span> @@ -72,7 +76,12 @@ Toggle.propTypes = { /** * Provide an id that unique represents the underlying `input` */ - id: PropTypes.string.isRequired, + id: PropTypes.string, + + /** + * Provide an name that unique represents the underlying `input` + */ + name: PropTypes.string.isRequired, /** * Specify whether the control is toggled @@ -94,6 +103,7 @@ Toggle.defaultProps = { defaultToggled: false, labelA: 'Off', labelB: 'On', + name: 'toggle', onToggle: () => {}, }; diff --git a/src/components/Toggle/Toggle.stories.js b/src/components/Toggle/Toggle.stories.js new file mode 100644 index 000000000..ba892ee9e --- /dev/null +++ b/src/components/Toggle/Toggle.stories.js @@ -0,0 +1,27 @@ +import React from 'react'; +import markdown from './README.mdx'; +import { action } from '@storybook/addon-actions'; +import Toggle from '.'; + +export default { + title: 'Components/Toggle', + component: Toggle, + parameters: { + componentSubtitle: 'Component', + mdx: markdown, + }, +}; + +export const Regular = (args) => <Toggle {...args} />; + +const description = ` +You can customize the content by using \`BannerNavigation\`. +`; + +Regular.story = { + parameters: { + docs: { + storyDescription: description, + }, + }, +}; diff --git a/src/documentation/Credits/Credits-story.js b/src/documentation/Credits/Credits.js similarity index 86% rename from src/documentation/Credits/Credits-story.js rename to src/documentation/Credits/Credits.js index 59d51d3de..c1236d0d4 100644 --- a/src/documentation/Credits/Credits-story.js +++ b/src/documentation/Credits/Credits.js @@ -1,18 +1,11 @@ -/* eslint-disable no-console */ - import React from 'react'; import { storiesOf } from '@storybook/react'; import Link from '../../components/Link'; import Page from '../Page'; -storiesOf('Getting started|Getting started', module) - .addParameters({ - options: { showPanel: false, isToolshown: false, sort: 'zzzzzz' }, - }) - .add('Credits', () => ( - <Page - title="Credits & License" - subTitle="Introduction to the new WFP UI Kit"> +export default function Credits() { + return ( + <div> <p> <Link href="http://brand.manuals.wfp.org/" target="_blank"> The World Food Programme’s (WFP) Branding Guidance @@ -72,5 +65,6 @@ storiesOf('Getting started|Getting started', module) View package on npm </Link> </p> - </Page> - )); + </div> + ); +} diff --git a/src/documentation/Credits/Credits.stories.mdx b/src/documentation/Credits/Credits.stories.mdx new file mode 100644 index 000000000..492b5f0a8 --- /dev/null +++ b/src/documentation/Credits/Credits.stories.mdx @@ -0,0 +1,9 @@ +import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks'; +//import Table from './Table'; +import ReactDOMServer from 'react-dom/server'; + +<Meta title="Documentation/Credits" subtitle="dasdas" /> + +import Credits from './Credits'; + +<Credits /> diff --git a/src/documentation/Logos/Logos.stories.mdx b/src/documentation/Logos/Logos.stories.mdx index a63346f62..6280c33bb 100644 --- a/src/documentation/Logos/Logos.stories.mdx +++ b/src/documentation/Logos/Logos.stories.mdx @@ -4,9 +4,4 @@ import Logos from './Logos'; <Meta title="Documentation/Logo" /> -# Checkbox - -With `MDX` we can define a story for `Checkbox` right in the middle of our -markdown documentation. - <Logos /> diff --git a/src/documentation/PageSpeed.stories.mdx b/src/documentation/PageSpeed.stories.mdx new file mode 100644 index 000000000..6a387c922 --- /dev/null +++ b/src/documentation/PageSpeed.stories.mdx @@ -0,0 +1,34 @@ +import { Meta, Story, Preview } from '@storybook/addon-docs/blocks'; + +<Meta + title="Documentation/Page Speed" + parameters={{ + componentSubtitle: 'Component', + }} +/> + +| Connection | Description | Download speed | Latency | +| ---------- | -------------------------------------------------------- | -------------- | ----------------- | +| VSAT | around 400 WFP offices globally rely on VSAT connections | 50-60 Kbps | 600ms up to 700ms | +| 2G | Usually found in rural areas of developing countries | 250 Kbps | 300 ms | +| Slow 3G | Usually found in rural areas | 500 Kbps | 100 ms | +| Fast 3G | Usually found in cities | >1500 Kbps | 40 ms | +| WFP HQ | Internet speed in WFPs Headquarter | > 50.000 Kbps | 2m | + +### Test with Google Chrome + +The Network tab in `Chrome DevTools` has an option to faux throttle your network, so you can experience what your users might experience visiting your application on 3G, 2G and GPRS connections. Besides the obvious, it’s also useful for visualising how the page load. + +1. Open DevTools (`cmd` + `alt` + `i`) +2. Click the `Network` tab +3. Select which type of connection you want to imitate +4. Reload the page to see assets downloading at that connection speed + +You can make a custom Network Throttling Profile vor VSAT connections by clicking the option at the very top of the dropdown menu and use the values above. + +<!-- +#### How to test different page-Speeds + +1. For solutions that must work on VSAT connections (high latency) +2. For solutions that must work with poor connectivity (frequent loss of connectivity) +3. For solutions that are aimed at normal network conditions. --> diff --git a/src/globals/scss/_helper-mixins.scss b/src/globals/scss/_helper-mixins.scss index 4e4d7aa69..afcfb83cd 100755 --- a/src/globals/scss/_helper-mixins.scss +++ b/src/globals/scss/_helper-mixins.scss @@ -67,6 +67,14 @@ } } +@mixin input-error { + &:invalid, + &.#{$prefix}--input--invalid { + border-color: $support-01; + outline: 1px solid $support-01; + } +} + @mixin box-shadow($size: 'small') { // Large - For dropdowns @if ($size == 'large') { diff --git a/src/globals/scss/_theme-tokens.scss b/src/globals/scss/_theme-tokens.scss index 589e10903..6db613b37 100644 --- a/src/globals/scss/_theme-tokens.scss +++ b/src/globals/scss/_theme-tokens.scss @@ -68,6 +68,7 @@ // Checkbox $checkbox-border-width: 2px !default !global; + $checkbox-border-radius: 4px !default !global; // Code Snippet $snippet-background-color: $ui-01 !default !global; diff --git a/src/globals/scss/styles.scss b/src/globals/scss/styles.scss index 423e6c087..93dc750ee 100755 --- a/src/globals/scss/styles.scss +++ b/src/globals/scss/styles.scss @@ -72,7 +72,7 @@ $deprecations--message: 'Deprecated code was found, this code will be removed be @import '../../components/Select/select.scss'; @import '../../components/ReactDropzone/react-dropzone.scss'; @import '../../components/ReactSelect/react-select.scss'; -@import '../../components/TextInput/text-input.scss'; +//@import '../../components/TextInput/text-input.scss'; @import '../../components/TextArea/text-area.scss'; @import '../../components/NumberInput/number-input.scss'; @import '../../components/Form/form.scss'; diff --git a/src/index.js b/src/index.js index fa23a85a9..58855b20d 100755 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ import '../src/globals/scss/styles.scss'; -/* + export { BannerNavigation, BannerNavigationItem, @@ -89,7 +89,7 @@ export Tabs from './components/Tabs'; export Tag from './components/Tag'; export TextArea from './components/TextArea'; export TextInput from './components/TextInput'; -export { tooltipStyle, tooltipStyleDark } from './components/Tooltip';*/ +export { tooltipStyle, tooltipStyleDark } from './components/Tooltip'; export Toggle from './components/Toggle'; export User from './components/User'; export Unit from './components/Unit'; diff --git a/webpack.config.js b/webpack.config.js index 7ed86172a..7a51babf3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,6 +2,53 @@ const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); +const cssesc = require('cssesc'); +const normalizePath = require('normalize-path'); +const { + stringifyRequest, + urlToRequest, + interpolateName, +} = require('loader-utils'); + +const filenameReservedRegex = /[<>:"/\\|?*\x00-\x1F]/g; +const reControlChars = /[\u0000-\u001f\u0080-\u009f]/g; +const reRelativePath = /^\.+/; +const reFileName = /([^\/]+)(?=\.\w+$)/g; + + +function getLocalIdent(loaderContext, localIdentName, localName, options) { + if (!options.context) { + // eslint-disable-next-line no-param-reassign + options.context = loaderContext.rootContext; + } + + var request = normalizePath( + path.relative(options.context || '', loaderContext.resourcePath) + ); + + const find = + loaderContext.resourcePath.match(reFileName)[0].replace('.module', '') === + localName; + // eslint-disable-next-line no-param-reassign + options.content = `${options.hashPrefix + request}+${unescape(localName)}`; + + // Using `[path]` placeholder outputs `/` we need escape their + // Also directories can contains invalid characters for css we need escape their too + let name = cssesc( + interpolateName(loaderContext, localIdentName, options) + // For `[hash]` placeholder + .replace(/^((-?[0-9])|--)/, '_$1') + .replace(filenameReservedRegex, '-') + .replace(reControlChars, '-') + .replace(reRelativePath, '-') + .replace(/\./g, '-') + .replace('-module', ''), + { isIdentifier: true } + ).replace(/\\\[local\\\]/gi, localName); + + if (find) name = name.replace('wfp--' + localName + '__', 'wfp--'); + return name; +} module.exports = { entry: './src/index.js', @@ -31,6 +78,26 @@ module.exports = { ], include: path.resolve(__dirname, './'), }, + { + test: /\.module.scss$/, + loaders: [ + require.resolve('style-loader'), + { + loader: require.resolve('css-loader'), + options: { + importLoaders: 1, + modules: { + mode: 'local', + localIdentName: 'wfp--[name]__[local]', + getLocalIdent, + context: path.resolve(__dirname, 'src'), + hashPrefix: 'my-custom-hash', + }, + }, + }, + require.resolve('sass-loader'), + ], + } /*{ test: /\.scssa$/, use: [