├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── pipeline.yml ├── .gitignore ├── .nvmrc ├── .prettierrc ├── .storybook ├── main.js ├── preview.js ├── styles.scss ├── theme.js └── webpack.config.js ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.json ├── netlify.toml ├── package-lock.json ├── package.json ├── scripts ├── babel.config.json ├── build.sh ├── check-package.js ├── package.json ├── publish-local.sh └── publish-npm.sh ├── src ├── govuk │ ├── components │ │ ├── accordion │ │ │ ├── accordion.story.js │ │ │ └── index.js │ │ ├── back-link │ │ │ ├── back-link.story.js │ │ │ └── index.js │ │ ├── breadcrumbs │ │ │ ├── breadcrumbs.story.js │ │ │ └── index.js │ │ ├── button │ │ │ ├── button.story.js │ │ │ ├── button.test.js │ │ │ └── index.js │ │ ├── character-count │ │ │ ├── character-count.story.js │ │ │ ├── character-count.test.js │ │ │ └── index.js │ │ ├── checkboxes │ │ │ ├── checkboxes.story.js │ │ │ ├── checkboxes.test.js │ │ │ └── index.js │ │ ├── cookie-banner │ │ │ ├── cookie-banner.story.js │ │ │ └── index.js │ │ ├── date-input │ │ │ ├── date-input.story.js │ │ │ ├── date-input.test.js │ │ │ └── index.js │ │ ├── details │ │ │ ├── details.story.js │ │ │ └── index.js │ │ ├── error-message │ │ │ ├── error-message.story.js │ │ │ └── index.js │ │ ├── error-summary │ │ │ ├── error-summary.story.js │ │ │ └── index.js │ │ ├── fieldset │ │ │ ├── fieldset.story.js │ │ │ └── index.js │ │ ├── file-upload │ │ │ ├── file-upload.story.js │ │ │ ├── file-upload.test.js │ │ │ └── index.js │ │ ├── footer │ │ │ ├── footer.story.js │ │ │ └── index.js │ │ ├── header │ │ │ ├── header.story.js │ │ │ └── index.js │ │ ├── hint │ │ │ ├── hint.story.js │ │ │ └── index.js │ │ ├── input │ │ │ ├── index.js │ │ │ ├── input.story.js │ │ │ └── input.test.js │ │ ├── inset-text │ │ │ ├── index.js │ │ │ └── inset-text.story.js │ │ ├── label │ │ │ ├── index.js │ │ │ └── label.story.js │ │ ├── notification-banner │ │ │ ├── index.js │ │ │ └── notification-banner.story.js │ │ ├── panel │ │ │ ├── index.js │ │ │ └── panel.story.js │ │ ├── phase-banner │ │ │ ├── index.js │ │ │ └── phase-banner.story.js │ │ ├── radios │ │ │ ├── index.js │ │ │ ├── radios.story.js │ │ │ └── radios.test.js │ │ ├── select │ │ │ ├── index.js │ │ │ ├── select.story.js │ │ │ └── select.test.js │ │ ├── skip-link │ │ │ ├── index.js │ │ │ └── skip-link.story.js │ │ ├── summary-list │ │ │ ├── index.js │ │ │ └── summary-list.story.js │ │ ├── table │ │ │ ├── index.js │ │ │ └── table.story.js │ │ ├── tabs │ │ │ ├── index.js │ │ │ └── tabs.story.js │ │ ├── tag │ │ │ ├── index.js │ │ │ └── tag.story.js │ │ ├── textarea │ │ │ ├── index.js │ │ │ ├── textarea.story.js │ │ │ └── textarea.test.js │ │ └── warning-text │ │ │ ├── index.js │ │ │ └── warning-text.story.js │ ├── index.js │ └── template │ │ ├── index.js │ │ └── template.story.js └── utils │ ├── Boolean.js │ ├── Link.js │ └── omitKey.js ├── tests ├── govuk-frontend-diff.test.js ├── mocks │ └── image.js ├── package.test.js └── utils │ └── jest.overrides.js └── utils ├── WithRef.js ├── processExampleData.js └── worstCaseData.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.js] 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.html] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.scss] 21 | indent_style = space 22 | indent_size = 2 23 | 24 | [*.md] 25 | trim_trailing_whitespace = false 26 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | storybook-static 2 | dist 3 | build-temp 4 | node_modules 5 | storybook-static 6 | !.storybook 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["wesbos"], 3 | "rules": { 4 | "prettier/prettier": "error", 5 | "import/no-cycle": "off", 6 | "import/no-extraneous-dependencies": "off", 7 | "import/no-unresolved": "off", 8 | "no-nested-ternary": "off", 9 | "no-shadow": "off", 10 | "react/prop-types": "off", 11 | "react/jsx-props-no-spreading": "off", 12 | "no-unused-vars": [ 13 | "error", 14 | { 15 | "argsIgnorePattern": "props|ref" 16 | } 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/pipeline.yml: -------------------------------------------------------------------------------- 1 | name: Pipeline 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Tests 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions/setup-node@v2.1.2 12 | with: 13 | node-version: 14 14 | - run: npm ci 15 | - name: Linting 16 | run: npm run lint 17 | - name: Run tests 18 | run: npm run test 19 | 20 | build: 21 | name: Build package 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions/setup-node@v2.1.2 26 | with: 27 | node-version: 14 28 | - run: npm ci 29 | - name: Build project 30 | run: npm run build 31 | - uses: actions/upload-artifact@v2.2.0 32 | with: 33 | name: dist 34 | path: dist 35 | 36 | pre-release: 37 | name: Pre release checks 38 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v2 42 | - uses: actions/setup-node@v2.1.2 43 | with: 44 | node-version: 14 45 | - run: npm ci 46 | - name: Check package.json 47 | run: node scripts/check-package.js --tag-name=${{ github.ref }} 48 | 49 | release: 50 | name: Release 51 | needs: [test, build, pre-release] 52 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@v2 56 | - uses: actions/download-artifact@v2 57 | with: 58 | name: dist 59 | path: dist 60 | - name: Create Release 61 | id: create_release 62 | uses: actions/create-release@v1.1.4 63 | env: 64 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 65 | with: 66 | tag_name: ${{ github.ref }} 67 | release_name: Release ${{ github.ref }} 68 | draft: false 69 | prerelease: false 70 | 71 | publish-npm: 72 | name: Publish to npm 73 | needs: release 74 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') 75 | runs-on: ubuntu-latest 76 | steps: 77 | - uses: actions/checkout@v2 78 | - uses: actions/setup-node@v2.1.2 79 | with: 80 | node-version: 14 81 | registry-url: 'https://registry.npmjs.org' 82 | - uses: actions/download-artifact@v2 83 | with: 84 | name: dist 85 | path: dist 86 | - name: Publish 87 | env: 88 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 89 | run: cd dist && npm publish 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | storybook-static 3 | .cache 4 | dist 5 | build-temp 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 14 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "endOfLine": "lf" 5 | } 6 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['../src/**/*.story.js'], 3 | addons: ['storybook-addon-jsx', '@storybook/addon-actions'], 4 | }; 5 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import { configure, addDecorator, addParameters } from '@storybook/react'; 2 | import { jsxDecorator } from 'storybook-addon-jsx'; 3 | import theme from './theme'; 4 | import './styles.scss'; 5 | 6 | addDecorator(jsxDecorator); 7 | 8 | addParameters({ 9 | options: { 10 | theme, 11 | storySort: (a, b) => 12 | a[1].kind === b[1].kind 13 | ? 0 14 | : a[1].id.localeCompare(b[1].id, undefined, { numeric: true }), 15 | }, 16 | }); 17 | 18 | // Simulate the js-enabled class that govuk frontend template adds to the page 19 | configure(() => document.body.classList.add('js-enabled'), module); 20 | -------------------------------------------------------------------------------- /.storybook/styles.scss: -------------------------------------------------------------------------------- 1 | $govuk-assets-path: '../node_modules/govuk-frontend/govuk/assets/'; 2 | $govuk-font-family: 'Helvetica', 'Arial', sans-serif; 3 | $govuk-new-link-styles: true; 4 | @import 'node_modules/govuk-frontend/govuk/all'; 5 | -------------------------------------------------------------------------------- /.storybook/theme.js: -------------------------------------------------------------------------------- 1 | import { create } from '@storybook/theming/create'; 2 | 3 | export default create({ 4 | base: 'light', 5 | 6 | colorPrimary: 'hotpink', 7 | colorSecondary: '#1d70b8', 8 | 9 | // UI 10 | appBg: 'white', 11 | appContentBg: 'white', 12 | appBorderColor: '#0b0c0c', 13 | appBorderRadius: 0, 14 | 15 | // Typography 16 | fontBase: '"Open Sans", sans-serif', 17 | fontCode: 'monospace', 18 | 19 | // Text colors 20 | textColor: '#0b0c0c', 21 | textInverseColor: 'rgba(0,0,0,0.9)', 22 | 23 | // Toolbar default and active colors 24 | barTextColor: '#1d70b8', 25 | barSelectedColor: '#1d70b8', 26 | barBg: '#f8f8f8', 27 | 28 | // Form colors 29 | inputBg: 'white', 30 | inputBorder: '#0b0c0c', 31 | inputTextColor: '#0b0c0c', 32 | inputBorderRadius: 0, 33 | 34 | brandTitle: 'govuk-react-jsx', 35 | brandUrl: 'https://github.com/surevine/govuk-react-jsx', 36 | // brandImage: 'https://placehold.it/350x150' 37 | }); 38 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // eslint-disable-next-line no-unused-vars 4 | module.exports = async ({ config, mode }) => { 5 | // `mode` has a value of 'DEVELOPMENT' or 'PRODUCTION' 6 | // You can change the configuration based on that. 7 | // 'PRODUCTION' is used when building the static version of storybook. 8 | 9 | config.module.rules = config.module.rules.filter( 10 | (rule) => !rule.test.test('.scss') 11 | ); 12 | 13 | // SCSS 14 | config.module.rules.push({ 15 | test: /\.scss$/, 16 | loaders: ['style-loader', 'css-loader', 'sass-loader'], 17 | include: path.resolve(__dirname, '../'), 18 | }); 19 | 20 | config.watchOptions = { 21 | poll: 1000, // Because I have to run on windows at the moment :-( 22 | }; 23 | 24 | return config; 25 | }; 26 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Chrome", 6 | "request": "launch", 7 | "type": "chrome", 8 | "url": "http://localhost:6006", 9 | "webRoot": "${workspaceFolder}" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | #### Fixes 6 | 7 | #### Features 8 | 9 | #### Breaking changes 10 | 11 | --- 12 | 13 | ## Releases 14 | 15 | ### v7.1.0 16 | 17 | #### Features 18 | 19 | - React 18 support (Simply by loosening the peer dependency requirement). 20 | 21 | ### v7.0.1 22 | 23 | #### Fixes 24 | 25 | - Update to govuk-frontend@4.0.1 (https://github.com/surevine/govuk-react-jsx/pull/144) 26 | - See https://github.com/alphagov/govuk-frontend/releases/tag/v4.0.1 for full release notes 27 | 28 | ### v7.0.0 29 | 30 | #### Fixes 31 | 32 | - Add null check to ref before calling govuk-frontend radios or checkboxes JS (https://github.com/surevine/govuk-react-jsx/pull/137/commits/4e7cd8421678adeb28c94812a1bc18051b9d1ab3) 33 | - Fixes https://github.com/surevine/govuk-react-jsx/issues/132 34 | 35 | #### Breaking changes 36 | 37 | - Update to govuk-frontend@4.0.0 (https://github.com/surevine/govuk-react-jsx/pull/137) 38 | - See https://github.com/alphagov/govuk-frontend/releases/tag/v4.0.0 for full release notes 39 | 40 | ### v6.2.1 41 | 42 | #### Fixes 43 | 44 | - Fix misaligned govuk crown in header. (Missed an instance in the previous release) (https://github.com/surevine/govuk-react-jsx/pull/128) 45 | - Fixes https://github.com/surevine/govuk-react-jsx/issues/125 46 | 47 | ### v6.2.0 48 | 49 | #### Features 50 | 51 | - Update to govuk-frontend@3.14.0 (https://github.com/surevine/govuk-react-jsx/pull/127) 52 | - See https://github.com/alphagov/govuk-frontend/releases/tag/v3.14.0 for full release notes 53 | 54 | #### Fixes 55 | 56 | - Fix misaligned govuk crown in header (https://github.com/surevine/govuk-react-jsx/pull/127) 57 | - Fixes https://github.com/surevine/govuk-react-jsx/issues/125 58 | 59 | ### v6.1.0 60 | 61 | #### Features 62 | 63 | - Update to govuk-frontend@3.13.0 (https://github.com/surevine/govuk-react-jsx/pull/118) 64 | - Add a 'none' option and 'or' divider to checkboxes (See [known issues in readme](https://github.com/surevine/govuk-react-jsx#known-issues) - this is somewhat of a non functional "stub" at the moment) 65 | - Change approach to fallback PNG in the header to fix blank data URI from triggering CSP error 66 | - See https://github.com/alphagov/govuk-frontend/releases/tag/v3.13.0 for full release notes 67 | 68 | ### v6.0.0 69 | 70 | #### Fixes 71 | 72 | - Fix badly destructured react list keys on some components (https://github.com/surevine/govuk-react-jsx/pull/114) 73 | - Fixes https://github.com/surevine/govuk-react-jsx/issues/113 74 | - Fix Template not passing text correctly to SkipLink component (https://github.com/surevine/govuk-react-jsx/pull/116) 75 | - Fixes https://github.com/surevine/govuk-react-jsx/issues/115 76 | 77 | #### Breaking changes 78 | 79 | - Table data structure changed in order to accomodate react list keys. Each row in the `rows` array now has a `cells` key instead of simply being a nested array of rows / cells. 80 | See the updated demos on the storybook. 81 | This breaking change was necessary in order to accomodate react list keys on table rows. 82 | 83 | #### Features 84 | 85 | - Updated to govuk-frontend@3.12.0 86 | Markup changes: 87 | 88 | - Add links styled as buttons to cookie banners 89 | - Add data-nosnippet to prevent cookie banner text appearing in Google Search snippets 90 | 91 | - See https://github.com/alphagov/govuk-frontend/releases/tag/v3.12.0 for full release notes 92 | 93 | ### v5.1.0 94 | 95 | #### Fixes 96 | 97 | - Fix overeager instantiation of govuk JS (https://github.com/surevine/govuk-react-jsx/pull/110) 98 | - Fixes https://github.com/surevine/govuk-react-jsx/issues/99 99 | 100 | #### Features 101 | 102 | - Updated to govuk-frontend@3.11.0 103 | - See https://github.com/alphagov/govuk-frontend/releases/tag/v3.11.0 for full release notes 104 | - Cookie banner component added 105 | 106 | ### v5.0.0 107 | 108 | - Further ErrorSummary focusing fixes 109 | The error summary no longer focuses itself at all when it is rendered - it is now fully up to the calling app to focus the error summary when appropriate. 110 | The ErrorSummary component accepts a ref which you can use to achieve this. 111 | A formik based demo of this is available in [https://github.com/surevine/govuk-react-jsx-examples/blob/master/src/forms/Formik.js](govuk-react-jsx-examples) 112 | 113 | ### v4.1.1 114 | 115 | #### Fixes 116 | 117 | - Remove overzealous focusing of ErrorSummary when list of errors changes. 118 | Component now accepts a ref which you can manually focus when appropriate (Such as when a form is submitted). 119 | A demo of this is available in [https://github.com/surevine/govuk-react-jsx-examples](govuk-react-jsx-examples) 120 | - Update govuk-frontend to 3.10.2 121 | See https://github.com/alphagov/govuk-frontend/releases/tag/v3.10.1 and https://github.com/alphagov/govuk-frontend/releases/tag/v3.10.2 fox full release notes 122 | 123 | ### v4.1.0 124 | 125 | #### Features 126 | 127 | - Larger release than usual since the 3.9.x update caused a few issues with testing. Therefore have jumped straight to 3.10.0 128 | 129 | - Updated to govuk-frontend@3.10.0 130 | See https://github.com/alphagov/govuk-frontend/releases/tag/v3.10.0 for full release notes 131 | - Customise input mode in the date component 132 | - New notification banner component added 133 | - Updated to govuk-frontend@3.9.1 134 | See https://github.com/alphagov/govuk-frontend/releases/tag/v3.9.1 for full release notes 135 | - Updated to govuk-frontend@3.9.0 136 | See https://github.com/alphagov/govuk-frontend/releases/tag/v3.9.0 for full release notes 137 | - Add input prefix and suffix. 138 | - Customise aria-label text in the header component - added `navigationLabel` and `menuButtonLabel` props 139 | - Ability to add navigation items without links 140 | - Switched test suite away from govuk-frontend-diff in favour of the fixtures now baked into govuk-frontend since 3.9.0 141 | - And as a result - have made many small fixes to pass the new test suite - nothing noteworthy, just small changes in edge case situations found in the new fixtures 142 | 143 | ### v4.0.3 144 | 145 | #### Fixes 146 | 147 | - Updated to govuk-frontend@3.8.1 148 | See https://github.com/alphagov/govuk-frontend/releases/tag/v3.8.1 for full release notes 149 | 150 | ### v4.0.2 151 | 152 | #### Fixes 153 | 154 | - Add webpack magic comments to checkboxes and radios (accidentally missed from previous release) 155 | - Tweak babel to allow dynamic imports through to the final package. Previously they were being resolved before being published. 156 | 157 | ### v4.0.1 158 | 159 | #### Fixes 160 | 161 | - Add webpack magic comments to optimize the dynamically imported govuk-frontend js 162 | 163 | ### v4.0.0 164 | 165 | #### Features 166 | 167 | - Updated to govuk-frontend@3.8.0 168 | See https://github.com/alphagov/govuk-frontend/releases/tag/v3.8.0 for full release notes 169 | - Switch hint from span to div so that it can render block-level elements as valid HTML. See https://github.com/alphagov/govuk-frontend/pull/1855 170 | - Document spellcheck -> spellCheck mapping to cater for new spellcheck param in upstream nunjucks. 171 | In reality no actual code changes for this one, just a documentation update. See https://github.com/alphagov/govuk-frontend/pull/1859 172 | - Switched to using [govuk-frontend-diff](https://github.com/surevine/govuk-frontend-diff) for testing duties 173 | 174 | #### Breaking changes 175 | 176 | - Upshot of the switch to govuk-frontend-diff was that it became necessary to dynamically import the GOVUK components which interact with the DOM, in order to enable Server Side Rendering for the tests. 177 | This is therefore marked up as a breaking change since it _may_ require changes to your Webpack config to support code splitting (Although create-react-app supports this out the box, in which case you don't need to do anything). 178 | See https://reactjs.org/docs/code-splitting.html#import for more details. 179 | This should pave the way for serverside rendering of the components although this isn't tested or formally supported yet (But may well work just fine...) 180 | 181 | ### v3.1.0 182 | 183 | #### Features 184 | 185 | - Updated to govuk-frontend@3.7.0 186 | See https://github.com/alphagov/govuk-frontend/releases/tag/v3.7.0 for full release notes. 187 | Only changes to the components are explicitly listed here. 188 | - Nunjucks implementation now allows html in each navigation item in the header component. This is already satisifed by the replacement of `text` and `html` params with a single `children` prop catering for both eventualities. See https://github.com/alphagov/govuk-frontend/pull/1819 189 | - You can now collapse the breadcrumb component on mobile using the new collapseOnMobile option. See https://github.com/alphagov/govuk-frontend/pull/1754 190 | 191 | ### v3.0.0 192 | 193 | #### Breaking changes 194 | 195 | - Moved the published package's dependencies to peerDependencies, to avoid issues with multiple copies of React etc being installed (Such as https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react). 196 | **If you aren't already, you will need to install compatible versions of govuk-frontend, react-router, react, react-router-dom, react-helmet since these are no longer installed as dependencies of this package** 197 | 198 | #### Fixes 199 | 200 | - Updated the package's govuk-frontend dependency to 3.6.0 - it was incorrectly pinned to an older version 201 | 202 | #### Features 203 | 204 | - Updated dependency on react-helmet to 6 205 | 206 | ### v2.0.1 207 | 208 | #### Fixes 209 | 210 | - Remove erroneous space before comma in footer OGL statement 211 | 212 | ### v2.0.0 213 | 214 | #### Breaking changes 215 | 216 | - Header links - `homepageUrl` and `serviceUrl` have become `homepageUrlHref` / `homepageUrlTo` and `serviceUrlHref` / `serviceUrlTo`, with the `To` variants being passed to a react-router `` the `Href` variants being a plain html `` tag 217 | 218 | ### v1.5.0 219 | 220 | #### Fixes 221 | 222 | - Use generic div element for tabspanel. See https://github.com/alphagov/govuk-frontend/pull/1746 223 | - Fix fallback logo being detected by chromes image description feature. See https://github.com/alphagov/govuk-frontend/pull/1724 224 | 225 | #### Features 226 | 227 | - Updated to govuk-frontend@3.6.0. See https://github.com/alphagov/govuk-frontend/releases/tag/v3.6.0 228 | 229 | ### v1.4.2 230 | 231 | #### Fixes 232 | 233 | - Fix error summary not focusing after first form submission. Now correctly focuses every time submit is pressed 234 | 235 | ### v1.4.1 236 | 237 | #### Fixes 238 | 239 | - Fix bug when using controlled inputs whereby Radios would switch from uncontrolled to controlled and throw a console warning. Caused by the new features added in v1.4.0 240 | 241 | ### v1.4.0 242 | 243 | #### Features 244 | 245 | - `defaultValue` top level prop added to radios component to complement the `value` prop and facilitate compatibility with form libraries dealing with uncontrolled form inputs (Such as react-hook-form) 246 | 247 | ### v1.3.0 248 | 249 | #### Features 250 | 251 | - `React.forwardRef` now added to all singular form components allowing you to pass `useRef` refs into the component props. Components that return multiple form elements now accept a `ref` key for each item in the `items` array prop. 252 | 253 | ### v1.2.0 254 | 255 | #### Fixes 256 | 257 | - Update the date input component to use input type=text inputmode=numeric. 258 | - Update checkboxes and radio buttons to include item hint classes on item hint. 259 | 260 | #### Features 261 | 262 | - Update to govuk-frontend@3.5.0. See https://github.com/alphagov/govuk-frontend/releases/tag/v3.5.0 for full notes. 263 | - Allow custom classes to be added to character count hint message 264 | 265 | #### Breaking changes 266 | 267 | ### v1.1.0 268 | 269 | #### Fixes 270 | 271 | More extensive test suite added, resulting in several minor fixes as follows 272 | 273 | - Fix date input, file upload, input, select, textarea, checkboxes & radios not receiving correct aria-describedby attribute 274 | - Fix date input, file upload, input, select, textarea, checkboxes & radios hints and error messages receiving incorrect ids 275 | - Fix incorrect casing of date input labels 276 | - Fix lack of colon after error message visually hidden text 277 | - Fix missing attributes from error summary links 278 | - Prevent assetPath prop making it through to header component in case someone passes it 279 | - Prevent tag attributes making it through from phase banner (behaviour now consistent with govuk-frontend) 280 | - Fix missing attributes from select options 281 | - Omit role attribute from checkboxes/radios fieldset to be consistent with govuk-frontend 282 | 283 | #### Features 284 | 285 | - onChange prop assigned directly to `` will now get passed through to the individual inputs (No need to put change handlers individually in the items now). This mirrors the behaviour already seen on other compound fields such as Radios and Checkboxes 286 | 287 | ### v1.0.1 288 | 289 | #### Fixes 290 | 291 | - Fix for uncontrolled -> controlled radio inputs caused by bug in value assignment 292 | - Slight tweak for Select component so that it accepts a top level value prop instead of individual selected booleans on each item to make it more React friendly 293 | 294 | --- 295 | 296 | ### v1.0.0 297 | 298 | Initial release 299 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Surevine 4 | Copyright (c) 2020 Andy Mantell 5 | Copyright (c) 2019 HM Land Registry 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GOV.UK React components (govuk-react-jsx) 2 | 3 | > This package is no longer maintained and will not receive updates bringing it beyond govuk-frontend 4.0.1. If you are using this in your project the simplest way forward is to copy and paste the components from here into your project allowing you to keep them up to date with govuk-frontend yourselves. 4 | 5 |
6 | View the readme 7 | 8 | [![govuk-frontend 4.0.1](https://img.shields.io/badge/govuk--frontend%20version-4.0.1-005EA5?logo=gov.uk&style=flat-square)](https://github.com/alphagov/govuk-frontend/releases/tag/v4.0.1) 9 | [![version](https://img.shields.io/npm/v/govuk-react-jsx.svg?style=flat-square)](https://www.npmjs.com/package/govuk-react-jsx) 10 | ![(Pipeline)](https://github.com/surevine/govuk-react-jsx/workflows/Pipeline/badge.svg) 11 | [![MIT License](https://img.shields.io/npm/l/govuk-react-jsx.svg?style=flat-square)](https://github.com/surevine/govuk-react-jsx/blob/main/LICENSE) 12 | 13 | _Please note - the version number of govuk-react-jsx is independent of the govuk-frontend version number._ 14 | 15 | View demo at https://govuk-react-jsx.netlify.app/ 16 | 17 | View example app at https://surevine.github.io/govuk-react-jsx-examples/ (for which the source code is at https://github.com/surevine/govuk-react-jsx-examples) 18 | 19 | ## WARNING 20 | > **If you do not need the characteristics of a Single Page App framework like React, please consider using something else**. [Government services should be progressively enhanced](https://www.gov.uk/service-manual/technology/using-progressive-enhancement), and [should function without JavaScript enabled](https://kryogenix.org/code/browser/everyonehasjs.html). 21 | > 22 | > If you have an explicit requirement that cannot be delivered effectively in a progressively enhanced manner then you might have a case for using React. If you have plans to build your React app in a progressively enhanced way, you might be fine. Whatever you do, be prepared to defend it at a service assessment. 23 | 24 | ## Quick install 25 | 26 | ``` 27 | $ npm install govuk-react-jsx 28 | ``` 29 | 30 | (See [installation and usage](#installation--usage) for more details) 31 | 32 | ## Motivation 33 | 34 | This repository contains govuk-frontend compatible React components. The aim of this package is to steer closely to govuk-frontend by consuming the CSS directly from the govuk-frontend npm package. And to strike a balance between mirroring the GOV.UK Nunjucks params vs ideomatic React props. 35 | 36 | This has the following benefits 37 | 38 | - Generated markup is identical to the output from the govuk-frontend macros. This allows us to benefit from the hard work that GDS has put into forming good markup patterns, including use of aria attributes. 39 | - Keeping up to date with upstream changes in govuk-frontend is as simple as updating the package.json version and mirroring any markup changes made. The test suite helps with this by comparing our output against the reference Nunjucks output - any differences constitute a test failure. 40 | - Anyone that knows the GOV.UK Nunjucks macros will quickly feel familiar with the structure of these components 41 | 42 | ## Comparison with govuk-react 43 | 44 | [govuk-react](https://github.com/govuk-react/govuk-react) is the other main option in this space. Naturally the first thing people ask is why one might use this repository instead of govuk-react. Here's my take on the matter: 45 | 46 | 47 | | govuk-react | govuk-react-jsx (This repository) | 48 | | ----------- | --------------------------------- | 49 | | An _implementation of_ the govuk design system.
CSS, JS and Markup patterns have all been rewritten from scratch.
Aria attributes missing. | Directly consumes the govuk-frontend CSS/JS and accurately mirrors their markup patterns, including Aria attributes | 50 | | Upstream CSS/JS changes in govuk-frontend need to be manually transferred across and/or rebuilt | Upstream CSS/JS changes are pulled in automatically. Only markup changes need to manually transferred (But are validated as correct by the test suite) | 51 | | Relatively complex code | Simpler code - just plain JSX ports of the Nunjucks | 52 | | Uses StyledComponents
Great if you like them and use them. But if you prefer a different library then you would end up needing both in your toolchain.
Increased maintenance burden on govuk-react team | Uses plain Sass compilation of the govuk-frontend code.
You are free to use a CSSinJS library of your choice for your own styles if you wish | 53 | | Cleaner component props since it has been designed from the ground up for React | Props mostly mirror the govuk-frontend Nunjucks params with some exceptions as below. This has been done in order to steer as closely to govuk-frontend as possible, and to facilitate the test suite checking the output against the original.

This is possibly the main argument against this repository and _for_ govuk-react. It's a tradeoff. One that is worth making _in my opinion_ but make your own call on that. | 54 | | More comprehensive set of components.
Includes components for headings, paragraphs, spacing etc | Only includes components that are direct equivalents of the Nunjucks templates in govuk-frontend.
(Although a future release is planned that will include grid, headings and paragraphs etc.)
Spacing classes will likely never be a component in this repository - some things like that I feel are already sufficiently well served by just using the CSS classes directly. This repository does not attempt to abstract you away from the fact that you are using govuk-frontend CSS. | 55 | | Allows code splitting of the styles, since they are defined in each component | No code splitting - all GOV.UK CSS is loaded. Although - it is relatively easy to omit the Scss files that you don't need so this is only a small downside | 56 | | No complications from integrating with other libraries | As per the React docs on [Integrating with other libraries](https://reactjs.org/docs/integrating-with-other-libraries.html) there is a very small chance that the integration of the GOV.UK JS might result in bugs if React tries to update a component that the GOV.UK JS has also updated. This is fairly unlikely but is something to be mindful of. | 57 | 58 | ## Assumptions 59 | 60 | These components assume you: 61 | 62 | - Have compiled the govuk-frontend scss and have included it in your page (create-react-app is able to compile the gov.uk Sass files) 63 | - Are using react-router 64 | - **Have read the exceptions below** 65 | 66 | ## Known issues 67 | 68 | - The "None of these" JavaScript initialised as part of govuk-frontend does not currently function with these components. Because external JS cannot influence the checked state of controlled components in React, the govuk-frontend approach does not work here. If you need this functionality in your service it would be best to implement it within the form framework you are using (E.g. Formik and the like). 69 | 70 | ## Exceptions 71 | 72 | Exceptions to the conformance with govuk-frontend nunjucks params are as follows: 73 | 74 | - Links - Anywhere that accepts an `href` / `text` combo of params to create a hyperlink, will also accept a `to` prop instead of `href`, which will be used in a react-router `` element. 75 | - Header links - `homepageUrl` and `serviceUrl` become `homepageUrlHref` / `homepageUrlTo` and `serviceUrlHref` / `serviceUrlTo`, with the `To` variants being passed to a react-router `` the `Href` variants being a plain html `
` tag 76 | - Anywhere that accepts an `html` or `text` param in Nunjucks will instead accept a `children` prop which should be passed either a string, or JSX. Params such as `summaryText` or `summaryHtml` become `summaryChildren` 77 | - `classes` becomes `className` 78 | - `spellcheck` becomes `spellCheck` 79 | - `inputmode` becomes `inputMode` 80 | - `describedBy` becomes `aria-describedby` 81 | - `colspan` and `rowspan` become `colSpan` and `rowSpan` 82 | - `autocomplete` becomes `autoComplete` 83 | - `ariaLabel` becomes `aria-label` 84 | - List keys - Anywhere that you specify an array of items such as a list of links, you may optionally specify a `reactListKey` for each item. This will be used instead of the index when doing `.map` over the items. React uses these keys internally to work out whether to re-render items. This is crucial for dynamic components where you might re-sort the list items for example. For static data it is less important and the key can be omitted. (See https://reactjs.org/docs/lists-and-keys.html#keys for more) 85 | (_The only exception to this rule is the tab component, where the tabs are already sufficiently keyed by id_) 86 | - The ` 159 | ``` 160 | 161 | In this code, `ref` will contain a reference to the actual DOM input element. You could use this to then call `.focus()` on the element. 162 | 163 | For components such as DateInput, Radios, Checkboxes and anywhere that uses an `items` prop to represent many form elements to be returned by the component, each object in the items array can accept a `ref` key which will then return a reference to the rendered form element for that item. 164 | 165 | Rather than adding them everywhere I have currently restricted it to form components where I can imagine a use case. Please open an issue if you need refs forwarded onto other types of elements - I may have missed a use case. 166 | 167 | ## Versioning 168 | 169 | This repository is versioned separately and follows standard semver procedures. 170 | 171 | ## Tests 172 | 173 | The test suite passes the example data from the govuk-frontend repository through the JSX components and compares the output with the reference output provided in govuk-frontend. Any differences here constitute a failure. 174 | 175 | Tests run in Github actions. 176 | 177 | ## Currently used by 178 | 179 | - Public Health England (See https://github.com/PublicHealthEngland/coronavirus-dashboard) 180 | - UKRI 181 | - Cabinet Office 182 | 183 | ## Contributors 184 | 185 | [Andy Mantell](https://github.com/andymantell) (Primary maintainer) 186 | 187 | [Dave Hudson](https://github.com/DaveHudson) 188 | 189 | [Mick Jones](https://github.com/mick-jones) (Helped to build the original JSX ports found at https://github.com/LandRegistry/govuk-react-components) 190 | 191 | ## Development sponsored by 192 | 193 | [Surevine](https://www.surevine.com) 194 | 195 | [HM Land Registry](https://www.gov.uk/government/organisations/land-registry) 196 | 197 |
198 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"], 3 | "plugins": [ 4 | "@babel/plugin-proposal-optional-chaining", 5 | "@babel/plugin-transform-runtime" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/" 4 | status = 200 5 | 6 | [[headers]] 7 | for = "/*" 8 | 9 | [headers.values] 10 | x-clacks-overhead = "GNU Terry Pratchett" 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "govuk-react-jsx", 3 | "version": "7.1.0", 4 | "description": "React component ports of govuk-frontend nunjucks macros. Directly consuming govuk-frontend CSS & JS.", 5 | "main": "src/govuk/index.js", 6 | "scripts": { 7 | "test": "jest --verbose", 8 | "storybook": "start-storybook -p 6006", 9 | "build-storybook": "build-storybook", 10 | "start": "npm run storybook", 11 | "lint": "eslint .", 12 | "lint:fix": "eslint . --fix", 13 | "deploy-demo": "npm run build-storybook && gh-pages -d storybook-static", 14 | "build": "./scripts/build.sh", 15 | "publish-local": "./scripts/publish-local.sh", 16 | "publish-npm": "./scripts/publish-npm.sh" 17 | }, 18 | "jest": { 19 | "testEnvironment": "jest-environment-jsdom-fourteen", 20 | "moduleNameMapper": { 21 | "govuk/assets/images/govuk-logotype-crown.png": "/tests/mocks/image.js", 22 | "govuk/assets/images/favicon.ico": "/tests/mocks/image.js", 23 | "govuk/assets/images/govuk-mask-icon.svg": "/tests/mocks/image.js", 24 | "govuk/assets/images/govuk-apple-touch-icon-180x180": "/tests/mocks/image.js", 25 | "govuk/assets/images/govuk-apple-touch-icon-167x167": "/tests/mocks/image.js", 26 | "govuk/assets/images/govuk-apple-touch-icon-152x152": "/tests/mocks/image.js", 27 | "govuk/assets/images/govuk-apple-touch-icon": "/tests/mocks/image.js", 28 | "govuk/assets/images/govuk-opengraph-image": "/tests/mocks/image.js" 29 | }, 30 | "setupFiles": [ 31 | "./tests/utils/jest.overrides.js" 32 | ] 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/surevine/govuk-react-jsx.git" 37 | }, 38 | "keywords": [ 39 | "govuk", 40 | "react", 41 | "jsx", 42 | "components", 43 | "govuk-frontend" 44 | ], 45 | "author": "Andy Mantell ", 46 | "license": "MIT", 47 | "bugs": { 48 | "url": "https://github.com/surevine/govuk-react-jsx/issues" 49 | }, 50 | "homepage": "https://github.com/surevine/govuk-react-jsx#readme", 51 | "dependencies": { 52 | "govuk-frontend": "4.0.1", 53 | "react": "^18.1.0", 54 | "react-dom": "^18.1.0", 55 | "react-helmet": "^6.1.0", 56 | "react-router": "^6.3.0", 57 | "react-router-dom": "^6.3.0", 58 | "yargs": "^16.2.0" 59 | }, 60 | "devDependencies": { 61 | "@babel/cli": "^7.12.16", 62 | "@babel/core": "^7.12.16", 63 | "@babel/node": "7.12.16", 64 | "@babel/plugin-proposal-optional-chaining": "^7.12.16", 65 | "@babel/plugin-transform-runtime": "^7.12.15", 66 | "@babel/runtime": "^7.12.13", 67 | "@markedjs/html-differ": "^3.0.4", 68 | "@starptech/prettyhtml": "^0.10.0", 69 | "@storybook/addon-actions": "^6.1.17", 70 | "@storybook/addons": "^6.1.17", 71 | "@storybook/react": "^6.1.17", 72 | "@testing-library/react": "^13.2.0", 73 | "babel-eslint": "^10.1.0", 74 | "babel-loader": "^8.2.2", 75 | "babel-plugin-file-loader": "^2.0.0", 76 | "body-parser": "^1.19.0", 77 | "cli-progress": "^3.9.0", 78 | "copy": "^0.3.2", 79 | "css-loader": "^5.0.2", 80 | "deep-iterator": "^1.1.0", 81 | "ent": "^2.2.0", 82 | "eslint": "7.19.0", 83 | "eslint-config-airbnb": "^18.2.1", 84 | "eslint-config-prettier": "^7.2.0", 85 | "eslint-config-wesbos": "1.0.1", 86 | "eslint-plugin-html": "^6.1.1", 87 | "eslint-plugin-import": "^2.22.1", 88 | "eslint-plugin-jsx-a11y": "^6.4.1", 89 | "eslint-plugin-prettier": "^3.3.1", 90 | "eslint-plugin-react": "^7.22.0", 91 | "eslint-plugin-react-hooks": "^4.2.0", 92 | "express": "^4.17.1", 93 | "file-loader": "^6.2.0", 94 | "glob": "^7.1.6", 95 | "got": "^11.8.1", 96 | "jest": "^26.6.3", 97 | "jest-environment-jsdom-fourteen": "^1.0.1", 98 | "js-yaml": "^4.0.0", 99 | "lodash": "^4.17.21", 100 | "mkdirp": "^1.0.4", 101 | "nunjucks": "^3.2.2", 102 | "prettier": "^2.2.1", 103 | "react-element-to-jsx-string": "14.3.2", 104 | "react-html-parser": "^2.0.2", 105 | "regenerator-runtime": "^0.13.7", 106 | "sass": "^1.32.7", 107 | "sass-loader": "^10.1.1", 108 | "semver": "^7.3.4", 109 | "storybook-addon-jsx": "^7.3.6", 110 | "style-loader": "^2.0.0", 111 | "webpack": "^4.46.0", 112 | "webpack-cli": "4.5.0" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /scripts/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "exclude": ["@babel/plugin-proposal-dynamic-import"] 7 | } 8 | ], 9 | "@babel/preset-react" 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-proposal-optional-chaining", 13 | "@babel/plugin-transform-runtime" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -rf dist 4 | mkdir -p build-temp 5 | # Not sure why have to be this explicit, the glob should sort it :-/ 6 | cd src 7 | copy govuk/index.js govuk/template/index.js govuk/components/**/index.js utils/*.js ../build-temp 8 | cd .. 9 | babel --config-file ./scripts/babel.config.json build-temp -d dist 10 | rm -rf build-temp 11 | 12 | PACKAGE_VERSION=$(node -p "require('./package.json').version") 13 | sed "s/PACKAGE_VERSION/${PACKAGE_VERSION}/g" scripts/package.json > dist/package.json 14 | 15 | cp CHANGELOG.md dist 16 | cp README.md dist 17 | -------------------------------------------------------------------------------- /scripts/check-package.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const yargs = require('yargs'); 3 | const packageJson = require('../package.json'); 4 | const packageLockJson = require('../package-lock.json'); 5 | 6 | const { argv } = yargs.option('tag-name'); 7 | 8 | assert.strictEqual( 9 | `refs/tags/v${packageJson.version}`, 10 | argv['tag-name'], 11 | "Version entry in package.json doesn't match the Github tag you are trying to release" 12 | ); 13 | 14 | assert.strictEqual( 15 | `refs/tags/v${packageLockJson.version}`, 16 | argv['tag-name'], 17 | "Version entry in package-lock.json doesn't match the Github tag you are trying to release" 18 | ); 19 | -------------------------------------------------------------------------------- /scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "govuk-react-jsx", 3 | "version": "PACKAGE_VERSION", 4 | "description": "", 5 | "main": "govuk/index.js", 6 | "scripts": {}, 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/surevine/govuk-react-jsx.git" 10 | }, 11 | "keywords": [ 12 | "govuk", 13 | "react", 14 | "jsx", 15 | "components", 16 | "govuk-frontend" 17 | ], 18 | "author": "Andy Mantell ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/surevine/govuk-react-jsx/issues" 22 | }, 23 | "homepage": "https://github.com/surevine/govuk-react-jsx#readme", 24 | "peerDependencies": { 25 | "govuk-frontend": "4.0.1", 26 | "react-router-dom": ">=5", 27 | "react-router": ">=5", 28 | "react": ">=16", 29 | "react-helmet": "6" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /scripts/publish-local.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./scripts/build.sh 4 | PACKAGE_VERSION=$(date +%s) 5 | sed "s/PACKAGE_VERSION/${PACKAGE_VERSION}.0.0/g" scripts/package.json > dist/package.json 6 | npm publish dist --registry http://localhost:4873/ 7 | -------------------------------------------------------------------------------- /scripts/publish-npm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./scripts/build.sh 4 | npm publish dist 5 | -------------------------------------------------------------------------------- /src/govuk/components/accordion/accordion.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import fixtures from 'govuk-frontend/govuk/components/accordion/fixtures.json'; 4 | import { Accordion } from '.'; 5 | import processExampleData from '../../../../utils/processExampleData'; 6 | 7 | const stories = storiesOf('accordion', module); 8 | 9 | for (const example of Object.values( 10 | processExampleData(fixtures.fixtures.filter((fixture) => !fixture.hidden)) 11 | )) { 12 | stories.add(example.name, () => ); 13 | } 14 | 15 | stories.add('with reactListKey specified', () => { 16 | const props = { ...fixtures.fixtures[0].options }; 17 | 18 | props.items = props.items.map((item, index) => ({ 19 | reactListKey: `your-stable-key-here-${index}`, 20 | ...item, 21 | })); 22 | return ; 23 | }); 24 | -------------------------------------------------------------------------------- /src/govuk/components/accordion/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | 3 | function Accordion(props) { 4 | const accordionRef = useRef(); 5 | const { headingLevel, items, className, id, ...attributes } = props; 6 | 7 | useEffect(() => { 8 | (async () => { 9 | if (typeof document !== 'undefined') { 10 | const { default: AccordionJS } = await import( 11 | /* webpackChunkName: "govuk-frontend-accordion" */ 12 | /* webpackMode: "lazy" */ 13 | /* webpackPrefetch: true */ 14 | 'govuk-frontend/govuk/components/accordion/accordion' 15 | ); 16 | 17 | if (accordionRef.current) { 18 | new AccordionJS(accordionRef.current).init(); 19 | } 20 | } 21 | })(); 22 | }, [accordionRef]); 23 | 24 | const HeadingLevel = headingLevel ? `h${headingLevel}` : 'h2'; 25 | 26 | const innerHtml = items.map((item, index) => { 27 | if (!item) { 28 | return; 29 | } 30 | 31 | return ( 32 |
38 |
39 | 40 | 44 | {item.heading.children} 45 | 46 | 47 | {item.summary ? ( 48 |
52 | {item.summary.children} 53 |
54 | ) : ( 55 | '' 56 | )} 57 |
58 |
63 | {item.content.children} 64 |
65 |
66 | ); 67 | }); 68 | return ( 69 |
76 | {innerHtml} 77 |
78 | ); 79 | } 80 | 81 | export { Accordion }; 82 | -------------------------------------------------------------------------------- /src/govuk/components/back-link/back-link.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import fixtures from 'govuk-frontend/govuk/components/back-link/fixtures.json'; 4 | import { BackLink } from '.'; 5 | import processExampleData from '../../../../utils/processExampleData'; 6 | 7 | const stories = storiesOf('back-link', module); 8 | 9 | for (const example of Object.values( 10 | processExampleData(fixtures.fixtures.filter((fixture) => !fixture.hidden)) 11 | )) { 12 | stories.add(example.name, () => ); 13 | } 14 | -------------------------------------------------------------------------------- /src/govuk/components/back-link/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from '../../../utils/Link'; 3 | 4 | function BackLink(props) { 5 | const { children, href, to, className, ...attributes } = props; 6 | const contents = children; 7 | 8 | return ( 9 | 15 | {contents} 16 | 17 | ); 18 | } 19 | 20 | BackLink.defaultProps = { 21 | href: '/', 22 | children: 'Back', 23 | }; 24 | 25 | export { BackLink }; 26 | -------------------------------------------------------------------------------- /src/govuk/components/breadcrumbs/breadcrumbs.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import fixtures from 'govuk-frontend/govuk/components/breadcrumbs/fixtures.json'; 4 | import { Breadcrumbs } from '.'; 5 | import processExampleData from '../../../../utils/processExampleData'; 6 | 7 | const stories = storiesOf('breadcrumbs', module); 8 | 9 | for (const example of Object.values( 10 | processExampleData(fixtures.fixtures.filter((fixture) => !fixture.hidden)) 11 | )) { 12 | stories.add(example.name, () => ); 13 | } 14 | 15 | stories.add('with reactListKey specified', () => { 16 | const props = { ...fixtures.fixtures[0].options }; 17 | 18 | props.items = props.items.map((item, index) => ({ 19 | reactListKey: `your-stable-key-here-${index}`, 20 | ...item, 21 | })); 22 | return ; 23 | }); 24 | -------------------------------------------------------------------------------- /src/govuk/components/breadcrumbs/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from '../../../utils/Link'; 3 | 4 | function Breadcrumbs(props) { 5 | const { items, className, collapseOnMobile, ...attributes } = props; 6 | const breadcrumbs = items 7 | ? items.map((item, index) => { 8 | const { href, to, reactListKey, children, ...itemAttributes } = item; 9 | 10 | return href || to ? ( 11 |
  • 15 | 21 | {children} 22 | 23 |
  • 24 | ) : ( 25 |
  • 30 | {children} 31 |
  • 32 | ); 33 | }) 34 | : null; 35 | 36 | return ( 37 |
    43 |
      {breadcrumbs}
    44 |
    45 | ); 46 | } 47 | 48 | export { Breadcrumbs }; 49 | -------------------------------------------------------------------------------- /src/govuk/components/button/button.story.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import fixtures from 'govuk-frontend/govuk/components/button/fixtures.json'; 4 | import { Button } from '.'; 5 | import processExampleData from '../../../../utils/processExampleData'; 6 | import { WithRef } from '../../../../utils/WithRef'; 7 | 8 | const stories = storiesOf('button', module); 9 | 10 | for (const example of Object.values( 11 | processExampleData(fixtures.fixtures.filter((fixture) => !fixture.hidden)) 12 | )) { 13 | stories.add(example.name, () => 120 | ); 121 | } else if (el === 'input') { 122 | if (!type) { 123 | buttonAttributes.type = 'submit'; 124 | } 125 | button = ( 126 | 127 | ); 128 | } 129 | 130 | return button; 131 | }); 132 | 133 | Button.displayName = 'Button'; 134 | 135 | export { Button }; 136 | -------------------------------------------------------------------------------- /src/govuk/components/character-count/character-count.story.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import fixtures from 'govuk-frontend/govuk/components/character-count/fixtures.json'; 4 | import { CharacterCount as CharacterCountComponent } from '.'; 5 | import processExampleData from '../../../../utils/processExampleData'; 6 | import { WithRef } from '../../../../utils/WithRef'; 7 | 8 | const stories = storiesOf('character-count', module); 9 | 10 | const CharacterCount = React.forwardRef((props, ref) => { 11 | const { value: initialValue, ...restProps } = props; 12 | const [value, setValue] = useState(initialValue); 13 | 14 | const onChangeHandler = (e) => { 15 | setValue(e.target.value); 16 | }; 17 | 18 | return ( 19 | 25 | ); 26 | }); 27 | 28 | CharacterCount.displayName = 'CharacterCount'; 29 | 30 | for (const example of Object.values( 31 | processExampleData( 32 | fixtures.fixtures.filter((fixture) => !fixture.hidden), 33 | 'character-component' 34 | ) 35 | )) { 36 | stories.add(example.name, () => ); 37 | } 38 | 39 | stories.add('with ref', () => ( 40 | 41 | )); 42 | -------------------------------------------------------------------------------- /src/govuk/components/character-count/character-count.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import fixtures from 'govuk-frontend/govuk/components/character-count/fixtures.json'; 4 | import { CharacterCount } from '.'; 5 | 6 | describe('character count', () => { 7 | it('correctly assigns a ref', () => { 8 | const ref = React.createRef(); 9 | const { container } = render( 10 | {}} 14 | /> 15 | ); 16 | 17 | expect(ref.current).toEqual(container.querySelector('textarea')); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/govuk/components/character-count/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import { Textarea, Hint } from '../..'; 3 | 4 | const CharacterCount = React.forwardRef((props, ref) => { 5 | const { 6 | id, 7 | className, 8 | maxlength, 9 | threshold, 10 | maxwords, 11 | errorMessage, 12 | countMessage, 13 | ...attributes 14 | } = props; 15 | 16 | const characterCountRef = useRef(); 17 | const characterCountInfoId = `${id}-info`; 18 | 19 | useEffect(() => { 20 | (async () => { 21 | if (typeof document !== 'undefined') { 22 | const { default: CharacterCountJS } = await import( 23 | /* webpackChunkName: "govuk-frontend-character-count" */ 24 | /* webpackMode: "lazy" */ 25 | /* webpackPrefetch: true */ 26 | 'govuk-frontend/govuk/components/character-count/character-count' 27 | ); 28 | 29 | if (characterCountRef.current) { 30 | new CharacterCountJS(characterCountRef.current).init(); 31 | } 32 | } 33 | })(); 34 | }, [characterCountRef]); 35 | 36 | return ( 37 |
    45 |