diff --git a/.storybook/_storybook.scss b/.storybook/_storybook.scss index 8be4b68..13521bf 100644 --- a/.storybook/_storybook.scss +++ b/.storybook/_storybook.scss @@ -39,3 +39,7 @@ .ribbonlink-demo { padding: 20px; } + +.fileupload-demo { + padding: 20px; +} diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts index cbadf6f..3558185 100644 --- a/src/__tests__/index.test.ts +++ b/src/__tests__/index.test.ts @@ -4,6 +4,7 @@ describe('Index', () => { it('contains all expected elements', () => { expect(Object.keys(index)).toEqual([ 'AccordionMenu', + 'FileUpload', 'MaskedInput', 'RibbonLink', 'SubNavigation', diff --git a/src/components.scss b/src/components.scss index 8639730..2717132 100644 --- a/src/components.scss +++ b/src/components.scss @@ -10,3 +10,6 @@ @import './components/tooltip/Tooltip'; @import './components/ribbon-link/RibbonLink'; @import './components/timeline/Timeline'; + +// FileUpload +@import './components/file-upload/FileUpload'; diff --git a/src/components/file-upload/FileUpload.scss b/src/components/file-upload/FileUpload.scss new file mode 100644 index 0000000..1a88c1d --- /dev/null +++ b/src/components/file-upload/FileUpload.scss @@ -0,0 +1,38 @@ +$nhsuk-input-border-colour: #0b0c0c; +$component-padding: nhsuk-spacing(1); + +.nhsuk-file-upload { + @include nhsuk-font($size: 19); + margin-left: -$component-padding; + padding: $component-padding; + + // The default file upload button in Safari does not + // support setting a custom font-size. Set `-webkit-appearance` + // to `button` to drop out of the native appearance so the + // font-size is set to 19px + // https://bugs.webkit.org/show_bug.cgi?id=224746 + &::-webkit-file-upload-button { + -webkit-appearance: button; + color: inherit; + font: inherit; + } + + &:focus { + outline: $nhsuk-focus-width solid $nhsuk-focus-color; + // Use `box-shadow` to add border instead of changing `border-width` + // (which changes element size) and since `outline` is already used for the + // yellow focus state. + box-shadow: inset 0 0 0 4px $nhsuk-input-border-colour; + } + + // Set "focus-within" to fix https://bugzilla.mozilla.org/show_bug.cgi?id=1430196 + // so that component receives focus in Firefox. + // This can't be set together with `:focus` as all versions of IE fail + // to recognise `focus-within` and don't set any styles from the block + // when it's a selector. + &:focus-within { + outline: $nhsuk-focus-width solid $nhsuk-focus-color; + + box-shadow: inset 0 0 0 4px $nhsuk-input-border-colour; + } +} diff --git a/src/components/file-upload/FileUpload.tsx b/src/components/file-upload/FileUpload.tsx new file mode 100644 index 0000000..2c77460 --- /dev/null +++ b/src/components/file-upload/FileUpload.tsx @@ -0,0 +1,21 @@ +import classNames from 'classnames'; +import { ErrorMessage, Hint, Label } from 'nhsuk-react-components'; +import React, { HTMLProps } from 'react'; + +interface FileUploadProps extends HTMLProps { + error?: string; + hint?: string; +} + +const FileUpload: React.FC = ({ error, hint, children, id, ...rest }) => { + return ( +
+ + {error && {error}} + {hint && {hint}} + +
+ ); +}; + +export default FileUpload; diff --git a/src/components/file-upload/__tests__/FileUpload.test.tsx b/src/components/file-upload/__tests__/FileUpload.test.tsx new file mode 100644 index 0000000..264a3c4 --- /dev/null +++ b/src/components/file-upload/__tests__/FileUpload.test.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { mount, shallow } from 'enzyme'; +import FileUpload from '../FileUpload'; + +describe('FileUpload', () => { + it('Matches snapshot', () => { + const component = shallow(Upload); + expect(component).toMatchSnapshot(); + component.unmount(); + }); + it('With Error', () => { + const component = mount(Upload); + expect(component).toMatchSnapshot(); + + const renderedComponent = component.render(); + expect(renderedComponent.find('span').prop('class')).toBe('nhsuk-error-message'); + + component.unmount(); + }); + it('With Hint', () => { + const component = mount(Upload); + expect(component).toMatchSnapshot(); + + const renderedComponent = component.render(); + expect(renderedComponent.find('span').prop('class')).toBe('nhsuk-hint'); + component.unmount(); + }); +}); diff --git a/src/components/file-upload/__tests__/__snapshots__/FileUpload.test.tsx.snap b/src/components/file-upload/__tests__/__snapshots__/FileUpload.test.tsx.snap new file mode 100644 index 0000000..f381fbe --- /dev/null +++ b/src/components/file-upload/__tests__/__snapshots__/FileUpload.test.tsx.snap @@ -0,0 +1,89 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FileUpload Matches snapshot 1`] = ` +
+ + +
+`; + +exports[`FileUpload With Error 1`] = ` + +
+ + + + + Error: + + something wrong + + + +
+
+`; + +exports[`FileUpload With Hint 1`] = ` + +
+ + + + Format: JPG + + + +
+
+`; diff --git a/src/components/file-upload/index.ts b/src/components/file-upload/index.ts new file mode 100644 index 0000000..1ad83ea --- /dev/null +++ b/src/components/file-upload/index.ts @@ -0,0 +1,3 @@ +import FileUpload from './FileUpload'; + +export default FileUpload; diff --git a/src/index.ts b/src/index.ts index 8567e3e..c1b2123 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export { default as AccordionMenu } from './components/accordion-menu'; +export { default as FileUpload } from './components/file-upload'; export { default as MaskedInput } from './components/masked-input'; export { default as RibbonLink } from './components/ribbon-link'; export { default as SubNavigation } from './components/sub-navigation'; diff --git a/stories/FileUpload.stories.tsx b/stories/FileUpload.stories.tsx new file mode 100644 index 0000000..2351d05 --- /dev/null +++ b/stories/FileUpload.stories.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { FileUpload } from '../src'; + +const stories = storiesOf('File Upload', module); + +stories + .add('Standard', () => ( +
+ Upload a file +
+ )) + .add('With Error', () => ( +
+ Upload a file +
+ )) + .add('With Hint', () => ( +
+ Upload a File +
+ ));