Browse Source

Lint JavaScript (#667)

* Add standard task for linting JavaScript

* Lint JavaScript

* Pin govuk-frontend to v4.0.1
pull/670/head
Paul Robert Lloyd 3 years ago committed by GitHub
parent
commit
adb32a1ed8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      app/frontend/application.js
  2. 6
      app/frontend/controllers/accessible_autocomplete_controller.js
  3. 2
      app/frontend/controllers/application.js
  4. 10
      app/frontend/controllers/conditional_filter_controller.js
  5. 30
      app/frontend/controllers/conditional_question_controller.js
  6. 6
      app/frontend/controllers/filter_layout_controller.js
  7. 8
      app/frontend/controllers/govukfrontend_controller.js
  8. 26
      app/frontend/controllers/index.js
  9. 33
      app/frontend/controllers/numeric_question_controller.js
  10. 26
      app/frontend/modules/filter_toggle.js
  11. 12
      babel.config.js
  12. 7
      lib/tasks/lint.rake
  13. 8
      package.json
  14. 34
      webpack.config.js
  15. 951
      yarn.lock

29
app/frontend/application.js

@ -1,22 +1,23 @@
// This file is automatically compiled by Webpack, along with any other files // This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in // present in this directory. You're encouraged to place your actual application
// a relevant structure within app/javascript and only use these pack files to reference // logic in a relevant structure within app/javascript and only use these pack
// that code so it'll be compiled. // files to reference that code so it'll be compiled.
// Polyfills for IE // Polyfills for IE
import "@webcomponents/webcomponentsjs" import '@webcomponents/webcomponentsjs'
import "core-js/stable" import 'core-js/stable'
import "regenerator-runtime/runtime" import 'regenerator-runtime/runtime'
import "@stimulus/polyfills" import '@stimulus/polyfills'
import "custom-event-polyfill" import 'custom-event-polyfill'
import "intersection-observer" import 'intersection-observer'
// //
import GOVUKFrontend from 'govuk-frontend'
import GOVUKPrototypeComponents from 'govuk-prototype-components'
import './styles/application.scss'
import './controllers'
require.context("govuk-frontend/govuk/assets") require.context('govuk-frontend/govuk/assets')
import GOVUKFrontend from "govuk-frontend"
import GOVUKPrototypeComponents from "govuk-prototype-components"
import "./styles/application.scss"
import "./controllers"
GOVUKFrontend.initAll() GOVUKFrontend.initAll()
GOVUKPrototypeComponents.initAll() GOVUKPrototypeComponents.initAll()

6
app/frontend/controllers/accessible_autocomplete_controller.js

@ -1,9 +1,9 @@
import { Controller } from "@hotwired/stimulus" import { Controller } from '@hotwired/stimulus'
import accessibleAutocomplete from "accessible-autocomplete" import accessibleAutocomplete from 'accessible-autocomplete'
import 'accessible-autocomplete/dist/accessible-autocomplete.min.css' import 'accessible-autocomplete/dist/accessible-autocomplete.min.css'
export default class extends Controller { export default class extends Controller {
connect() { connect () {
accessibleAutocomplete.enhanceSelectElement({ accessibleAutocomplete.enhanceSelectElement({
defaultValue: '', defaultValue: '',
selectElement: this.element selectElement: this.element

2
app/frontend/controllers/application.js

@ -1,4 +1,4 @@
import { Application } from "@hotwired/stimulus" import { Application } from '@hotwired/stimulus'
const application = Application.start() const application = Application.start()

10
app/frontend/controllers/conditional_filter_controller.js

@ -1,13 +1,13 @@
import { Controller } from "@hotwired/stimulus" import { Controller } from '@hotwired/stimulus'
export default class extends Controller { export default class extends Controller {
initialize() { initialize () {
this.clearIfHidden() this.clearIfHidden()
} }
clearIfHidden() { clearIfHidden () {
if(this.element.style["display"] == "none") { if (this.element.style.display === 'none') {
this.element.value = "" this.element.value = ''
} }
} }
} }

30
app/frontend/controllers/conditional_question_controller.js

@ -1,20 +1,20 @@
import { Controller } from "@hotwired/stimulus" import { Controller } from '@hotwired/stimulus'
export default class extends Controller { export default class extends Controller {
initialize() { initialize () {
this.displayConditional() this.displayConditional()
} }
displayConditional() { displayConditional () {
if(this.element.checked) { if (this.element.checked) {
let selectedValue = this.element.value const selectedValue = this.element.value
let conditional_for = JSON.parse(this.element.dataset.info) const conditionalFor = JSON.parse(this.element.dataset.info)
Object.entries(conditional_for).map(([targetQuestion, conditions]) => {
if(conditions.map(String).includes(String(selectedValue))) { Object.entries(conditionalFor).forEach(([targetQuestion, conditions]) => {
} else { if (!conditions.map(String).includes(String(selectedValue))) {
const textNumericInput = document.getElementById(`case-log-${targetQuestion.replaceAll("_","-")}-field`) const textNumericInput = document.getElementById(`case-log-${targetQuestion.replaceAll('_', '-')}-field`)
if (textNumericInput == null) { if (textNumericInput == null) {
const dateInputs = [1,2,3].map((idx) => { const dateInputs = [1, 2, 3].map((idx) => {
return document.getElementById(`case_log_${targetQuestion}_${idx}i`) return document.getElementById(`case_log_${targetQuestion}_${idx}i`)
}) })
this.clearDateInputs(dateInputs) this.clearDateInputs(dateInputs)
@ -26,11 +26,11 @@ export default class extends Controller {
} }
} }
clearTextNumericInput(input) { clearTextNumericInput (input) {
input.value = "" input.value = ''
} }
clearDateInputs(inputs) { clearDateInputs (inputs) {
inputs.forEach((input) => { input.value = "" }) inputs.forEach((input) => { input.value = '' })
} }
} }

6
app/frontend/controllers/filter_layout_controller.js

@ -1,8 +1,8 @@
import { Controller } from "@hotwired/stimulus"; import { Controller } from '@hotwired/stimulus'
import { FilterToggle } from "../modules/filter_toggle.js" import { FilterToggle } from '../modules/filter_toggle.js'
export default class extends Controller { export default class extends Controller {
connect() { connect () {
const filterToggle = new FilterToggle({ const filterToggle = new FilterToggle({
bigModeMediaQuery: '(min-width: 48.0625em)', bigModeMediaQuery: '(min-width: 48.0625em)',
closeButton: { closeButton: {

8
app/frontend/controllers/govukfrontend_controller.js

@ -1,9 +1,9 @@
import GOVUKFrontend from "govuk-frontend"; import GOVUKFrontend from 'govuk-frontend'
import GOVUKPrototypeComponents from "govuk-prototype-components" import GOVUKPrototypeComponents from 'govuk-prototype-components'
import { Controller } from "@hotwired/stimulus"; import { Controller } from '@hotwired/stimulus'
export default class extends Controller { export default class extends Controller {
connect() { connect () {
GOVUKFrontend.initAll() GOVUKFrontend.initAll()
GOVUKPrototypeComponents.initAll() GOVUKPrototypeComponents.initAll()
} }

26
app/frontend/controllers/index.js

@ -1,22 +1,22 @@
// Load all the controllers within this directory and all subdirectories. // Load all the controllers within this directory and all subdirectories.
// Controller files must be named *_controller.js. // Controller files must be named *_controller.js.
import { application } from "./application" import { application } from './application'
import AccessibleAutocompleteController from "./accessible_autocomplete_controller.js" import AccessibleAutocompleteController from './accessible_autocomplete_controller.js'
application.register("accessible-autocomplete", AccessibleAutocompleteController)
import ConditionalFilterController from "./conditional_filter_controller.js" import ConditionalFilterController from './conditional_filter_controller.js'
application.register("conditional-filter", ConditionalFilterController)
import ConditionalQuestionController from "./conditional_question_controller.js" import ConditionalQuestionController from './conditional_question_controller.js'
application.register("conditional-question", ConditionalQuestionController)
import GovukfrontendController from "./govukfrontend_controller.js" import GovukfrontendController from './govukfrontend_controller.js'
application.register("govukfrontend", GovukfrontendController)
import NumericQuestionController from "./numeric_question_controller.js" import NumericQuestionController from './numeric_question_controller.js'
application.register("numeric-question", NumericQuestionController)
import FilterLayoutController from "./filter_layout_controller.js" import FilterLayoutController from './filter_layout_controller.js'
application.register("filter-layout", FilterLayoutController) application.register('accessible-autocomplete', AccessibleAutocompleteController)
application.register('conditional-filter', ConditionalFilterController)
application.register('conditional-question', ConditionalQuestionController)
application.register('govukfrontend', GovukfrontendController)
application.register('numeric-question', NumericQuestionController)
application.register('filter-layout', FilterLayoutController)

33
app/frontend/controllers/numeric_question_controller.js

@ -1,27 +1,26 @@
import { Controller } from "@hotwired/stimulus" import { Controller } from '@hotwired/stimulus'
export default class extends Controller { export default class extends Controller {
connect() { connect () {
const affectedField = this.element.dataset.target; const affectedField = this.element.dataset.target
const targetQuestion = affectedField.split("case-log-")[1].split("-field")[0] const targetQuestion = affectedField.split('case-log-')[1].split('-field')[0]
const div = document.getElementById(targetQuestion + "_div"); const div = document.getElementById(targetQuestion + '_div')
div.style.display = "block"; div.style.display = 'block'
} }
calculateFields() { calculateFields () {
const affectedField = this.element.dataset.target; const affectedField = this.element.dataset.target
const fieldsToAdd = JSON.parse(this.element.dataset.calculated).map(x => `case-log-${x.replaceAll("_","-")}-field`); const fieldsToAdd = JSON.parse(this.element.dataset.calculated).map(x => `case-log-${x.replaceAll('_', '-')}-field`)
const valuesToAdd = fieldsToAdd.map(x => getFieldValue(x)).filter(x => x); const valuesToAdd = fieldsToAdd.map(x => getFieldValue(x)).filter(x => x)
const newValue = valuesToAdd.map(x => parseFloat(x)).reduce((a, b) => a + b, 0).toFixed(2); const newValue = valuesToAdd.map(x => parseFloat(x)).reduce((a, b) => a + b, 0).toFixed(2)
const elementToUpdate = document.getElementById(affectedField); const elementToUpdate = document.getElementById(affectedField)
elementToUpdate.value = newValue; elementToUpdate.value = newValue
} }
} }
let getFieldValue = (field) => { const getFieldValue = (field) => {
const elementFieldToAdd= document.getElementById(field) const elementFieldToAdd = document.getElementById(field)
if (elementFieldToAdd) { if (elementFieldToAdd) {
return elementFieldToAdd.value return elementFieldToAdd.value
} }
return document.getElementById(`${field}-error`).value return document.getElementById(`${field}-error`).value
} }

26
app/frontend/modules/filter_toggle.js

@ -25,7 +25,7 @@ export class FilterToggle {
} }
enableSmallMode () { enableSmallMode () {
this.options.filter.container.setAttribute("tabindex", "-1") this.options.filter.container.setAttribute('tabindex', '-1')
this.hideMenu() this.hideMenu()
this.addMenuButton() this.addMenuButton()
this.addCloseButton() this.addCloseButton()
@ -33,8 +33,8 @@ export class FilterToggle {
addCloseButton () { addCloseButton () {
if (this.options.closeButton) { if (this.options.closeButton) {
this.closeButton = document.createElement("button") this.closeButton = document.createElement('button')
this.closeButton.classList.add("app-filter__close") this.closeButton.classList.add('app-filter__close')
this.closeButton.innerText = this.options.closeButton.text this.closeButton.innerText = this.options.closeButton.text
this.closeButton.type = 'button' this.closeButton.type = 'button'
this.closeButton.addEventListener('click', this.onCloseClick.bind(this)) this.closeButton.addEventListener('click', this.onCloseClick.bind(this))
@ -56,13 +56,13 @@ export class FilterToggle {
} }
addMenuButton () { addMenuButton () {
this.menuButton = document.createElement("button") this.menuButton = document.createElement('button')
this.menuButton.setAttribute("aria-expanded", "false") this.menuButton.setAttribute('aria-expanded', 'false')
this.menuButton.setAttribute("aria-has-popup", "true") this.menuButton.setAttribute('aria-has-popup', 'true')
this.menuButton.classList.add("govuk-button", this.options.toggleButton.classes, "app-filter-toggle__button") this.menuButton.classList.add('govuk-button', this.options.toggleButton.classes, 'app-filter-toggle__button')
this.menuButton.innerText = this.options.toggleButton.showText this.menuButton.innerText = this.options.toggleButton.showText
this.menuButton.type = "button" this.menuButton.type = 'button'
this.menuButton.addEventListener("click", this.onMenuButtonClick.bind(this)) this.menuButton.addEventListener('click', this.onMenuButtonClick.bind(this))
this.options.toggleButton.container.prepend(this.menuButton) this.options.toggleButton.container.prepend(this.menuButton)
} }
@ -76,18 +76,18 @@ export class FilterToggle {
hideMenu () { hideMenu () {
if (this.menuButton) { if (this.menuButton) {
this.menuButton.setAttribute("aria-expanded", "false") this.menuButton.setAttribute('aria-expanded', 'false')
this.menuButton.innerText = this.options.toggleButton.showText this.menuButton.innerText = this.options.toggleButton.showText
} }
this.options.filter.container.setAttribute("hidden", true) this.options.filter.container.setAttribute('hidden', true)
} }
showMenu () { showMenu () {
if (this.menuButton) { if (this.menuButton) {
this.menuButton.setAttribute("aria-expanded", "true") this.menuButton.setAttribute('aria-expanded', 'true')
this.menuButton.innerText = this.options.toggleButton.hideText this.menuButton.innerText = this.options.toggleButton.hideText
} }
this.options.filter.container.removeAttribute("hidden") this.options.filter.container.removeAttribute('hidden')
} }
onMenuButtonClick () { onMenuButtonClick () {

12
babel.config.js

@ -1,9 +1,9 @@
module.exports = function(api) { module.exports = function (api) {
var validEnv = ['development', 'test', 'production'] const validEnv = ['development', 'test', 'production']
var currentEnv = api.env() const currentEnv = api.env()
var isDevelopmentEnv = api.env('development') const isDevelopmentEnv = api.env('development')
var isProductionEnv = api.env('production') const isProductionEnv = api.env('production')
var isTestEnv = api.env('test') const isTestEnv = api.env('test')
if (!validEnv.includes(currentEnv)) { if (!validEnv.includes(currentEnv)) {
throw new Error( throw new Error(

7
lib/tasks/lint.rake

@ -8,10 +8,15 @@ task erblint: :environment do
sh "bundle exec erblint --lint-all" sh "bundle exec erblint --lint-all"
end end
desc "Run Standard"
task standard: :environment do
sh "yarn standard"
end
desc "Run Stylelint" desc "Run Stylelint"
task stylelint: :environment do task stylelint: :environment do
sh "yarn stylelint app/frontend/styles" sh "yarn stylelint app/frontend/styles"
end end
desc "Run all the linters" desc "Run all the linters"
task lint: %i[rubocop erblint stylelint] task lint: %i[rubocop erblint standard stylelint]

8
package.json

@ -20,7 +20,7 @@
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"custom-event-polyfill": "^1.0.7", "custom-event-polyfill": "^1.0.7",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"govuk-frontend": "^4.0.1", "govuk-frontend": "4.0.1",
"govuk-prototype-components": "^0.1.5", "govuk-prototype-components": "^0.1.5",
"html5shiv": "^3.7.3", "html5shiv": "^3.7.3",
"intersection-observer": "^0.12.0", "intersection-observer": "^0.12.0",
@ -36,6 +36,7 @@
"version": "0.1.0", "version": "0.1.0",
"devDependencies": { "devDependencies": {
"are-you-es5": "^2.1.2", "are-you-es5": "^2.1.2",
"standard": "^17.0.0",
"stylelint": "^14.7.1", "stylelint": "^14.7.1",
"stylelint-config-gds": "^0.2.0" "stylelint-config-gds": "^0.2.0"
}, },
@ -51,6 +52,11 @@
"last 1 safari version" "last 1 safari version"
] ]
}, },
"standard": {
"ignore": [
"app/frontend/vendor/*.js"
]
},
"stylelint": { "stylelint": {
"extends": "stylelint-config-gds/scss" "extends": "stylelint-config-gds/scss"
}, },

34
webpack.config.js

@ -1,18 +1,18 @@
const path = require("path") const path = require('node:path')
const webpack = require("webpack") const webpack = require('webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts') const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts')
const CopyPlugin = require("copy-webpack-plugin"); const CopyPlugin = require('copy-webpack-plugin')
const mode = process.env.NODE_ENV === 'development' ? 'development' : 'production' const mode = process.env.NODE_ENV === 'development' ? 'development' : 'production'
module.exports = { module.exports = {
mode, mode,
devtool: "source-map", devtool: 'source-map',
entry: { entry: {
application: [ application: [
"./app/frontend/application.js", './app/frontend/application.js'
] ]
}, },
module: { module: {
@ -24,9 +24,9 @@ module.exports = {
path.resolve(__dirname, 'node_modules/@stimulus/polyfills'), path.resolve(__dirname, 'node_modules/@stimulus/polyfills'),
path.resolve(__dirname, 'node_modules/@rails/actioncable'), path.resolve(__dirname, 'node_modules/@rails/actioncable'),
path.resolve(__dirname, 'node_modules/chartjs'), path.resolve(__dirname, 'node_modules/chartjs'),
path.resolve(__dirname, 'app/frontend'), path.resolve(__dirname, 'app/frontend')
], ],
use: ['babel-loader'], use: ['babel-loader']
}, },
{ {
test: /\.(png|jpe?g|gif|eot|woff|woff2|ttf|svg|ico)$/i, test: /\.(png|jpe?g|gif|eot|woff|woff2|ttf|svg|ico)$/i,
@ -34,9 +34,9 @@ module.exports = {
}, },
{ {
test: /\.(scss|css)/i, test: /\.(scss|css)/i,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'], use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
} }
], ]
}, },
resolve: { resolve: {
alias: { alias: {
@ -46,11 +46,11 @@ module.exports = {
modules: ['node_modules', 'node_modules/govuk-frontend/govuk'] modules: ['node_modules', 'node_modules/govuk-frontend/govuk']
}, },
output: { output: {
filename: "[name].js", filename: '[name].js',
// we must set publicPath to an empty value to override the default of // we must set publicPath to an empty value to override the default of
// auto which doesn't work in IE11 // auto which doesn't work in IE11
publicPath: '', publicPath: '',
path: path.resolve(__dirname, "app/assets/builds"), path: path.resolve(__dirname, 'app/assets/builds')
}, },
plugins: [ plugins: [
new RemoveEmptyScriptsPlugin(), new RemoveEmptyScriptsPlugin(),
@ -58,12 +58,12 @@ module.exports = {
new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }), new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ from: "node_modules/govuk-frontend/govuk/assets/images", to: "images" }, { from: 'node_modules/govuk-frontend/govuk/assets/images', to: 'images' },
{ from: "node_modules/govuk-frontend/govuk/assets/fonts", to: "fonts" }, { from: 'node_modules/govuk-frontend/govuk/assets/fonts', to: 'fonts' },
{ from: "node_modules/html5shiv/dist/html5shiv.min.js", to: "vendor" }, { from: 'node_modules/html5shiv/dist/html5shiv.min.js', to: 'vendor' },
{ from: "app/frontend/vendor/outerHTML.js", to: "vendor" }, { from: 'app/frontend/vendor/outerHTML.js', to: 'vendor' },
{ from: "app/frontend/vendor/polyfill-output-value.js", to: "vendor" } { from: 'app/frontend/vendor/polyfill-output-value.js', to: 'vendor' }
], ]
}) })
] ]
} }

951
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save