かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その11 )( PostCSS で common.css を minify する + autoprefixer, stylelint を導入する )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その10 )( 各画面の HTML を作成する3 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • これまでは cpx パッケージで watch してコピーするだけでしたが、PostCSS で watch + コピー + minify するように変更します。
    • postcss の autoprefixer, stylelint プラグインも導入します。

参照したサイト・書籍

  1. PostCSS
    https://github.com/postcss/postcss

  2. PostCSS CLI
    https://github.com/postcss/postcss-cli

  3. cssnano
    https://github.com/ben-eb/cssnano

  4. Autoprefixer
    https://github.com/postcss/autoprefixer

  5. Browserslist
    https://github.com/ai/browserslist

  6. stylelint
    https://github.com/stylelint/stylelint

  7. stylelint-config-standard
    https://github.com/stylelint/stylelint-config-standard

  8. ‘at-rule-empty-line-before’ do not accept except + ignore
    https://github.com/stylelint/stylelint/issues/691

  9. PostCSS まとめ
    http://qiita.com/morishitter/items/4a04eb144abf49f41d7d

  10. Sassを捨ててPostCSSに移行したのでそのときの工程メモ
    http://qiita.com/nabeliwo/items/0aeea21e95f3fbab3955

  11. ここがすごいぞ! stylelint!
    http://qiita.com/inuscript/items/ff4f6972c988afbec3a8

  12. PostCSSとstylelintの環境構築
    http://qiita.com/buchiya4th/items/01b4ad050b7c59b48539

目次

  1. はじめに
  2. PostCSS と cssnano プラグインをインストールして minify してみる
    1. PostCSS をインストールする
    2. cssnano プラグインをインストールする
    3. postcss.config.js を作成する
    4. npm-scripts を追加する
    5. 動作確認する
    6. npm-scripts を変更して正式に取り込む
    7. 動作確認する(その2)
  3. Autoprefixer プラグインを追加する
    1. Autoprefixer プラグインをインストールする
    2. postcss.config.js を変更する
    3. 動作確認する
  4. stylelint プラグインを追加する
    1. stylelint プラグインと stylelint-config-standard をインストールする
    2. postcss.config.js を変更する
    3. stylelint.config.js を作成する
    4. 動作確認する
    5. stylelint で出力されたメッセージに対応する
  5. 次回は。。。

手順

はじめに

作成している common.css を単にコピーするだけでなく圧縮(minify)もしたいと思い調べたところ、PostCSS を使えば出来るようなので、試してみます。

PostCSS ではいろいろなプラグインを組み合わせて使用しますが、今回は以下のプラグインを入れてみます。

  • Autoprefixer(必要なベンダープリフィックスを自動で付与する)
  • cssnano(minify)
  • stylelint(CSSリンター)

PostCSS と cssnano プラグインをインストールして minify してみる

PostCSS をインストールする

npm install --save-dev postcss-cli コマンドを実行してインストールします。

f:id:ksby:20170730191424p:plain f:id:ksby:20170730191623p:plain

cssnano プラグインをインストールする

npm install --save-dev cssnano コマンドを実行してインストールします。

f:id:ksby:20170805011113p:plain f:id:ksby:20170805011246p:plain f:id:ksby:20170805011400p:plain f:id:ksby:20170805011510p:plain

postcss.config.js を作成する

プロジェクトのルートディレクトリに postcss.config.js を新規作成し、以下の内容を記述します。

module.exports = {
    plugins: [
        require('cssnano')({
            preset: 'default'
        })
    ]
};

npm-scripts を追加する

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........
    "copy:css:watch": "cpx src/main/assets/css/**/* src/main/resources/static/css -w",
    "postcss:watch": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css -w --poll",
    "server": "run-p copy:css:watch webpack:watch browser-sync:start"
  },
  • "postcss:watch": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css -w --poll" を追加します。-w オプションだけだとなぜかうまく watch してくれない(数回変更を検知するとそれ以降検知しなくなる時がある)ので、--poll オプションも追加しました。
    ※この後の動作確認の時はまだ --poll オプションは付けていません。うまく動かない時があることに気付いて後から付けています。
  • postcss を実行した同じディレクトリに postcss.config.js があると自動で読み込んでくれるようなので -c postcss.config.js は付けません。

動作確認する

src/main/resources/static の下の css ディレクトリを削除します。

npm run postcss:watch コマンドを実行します。

f:id:ksby:20170730231735p:plain

src/main/resources/static の下を見ると css/common.min.css が生成されており、

f:id:ksby:20170730231349p:plain

common.min.css を開くと minify されていました。

f:id:ksby:20170730231845p:plain

src/main/assets/css/common.cssbackground-color: ivory;background-color: snow; に変更すると、

f:id:ksby:20170730232034p:plain

再び Finished src\main\assets\css\common.css のメッセージが出力されて、

f:id:ksby:20170730232154p:plain

src/main/resources/static/css/common.min.css に反映されます。

f:id:ksby:20170730232357p:plain

うまく動作しているようです。ただし、以下1点注意があります。

  • IntelliJ IDEA で src/main/assets/css/common.css を変更して、IntelliJ IDEA を Active な Window にしたまま src/main/resources/static/css/common.min.css を表示しても反映はされませんでした。一度 IntelliJ IDEA から他のプロセスに Active な Window を切り替えないと(マウスか Alt+Tab 等のキーで別の Window を Active にしないと)反映されませんでした。

npm-scripts を変更して正式に取り込む

package.json の scripts を以下のように変更します。

  "scripts": {
    ..........
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons",
    "postcss:watch": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css -w --poll",
    "webpack": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync": "browser-sync",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "server": "run-p postcss:watch webpack:watch browser-sync:start"
  },
  • "copy:css:watch": "cpx src/main/assets/css/**/* src/main/resources/static/css -w" を削除します。
  • "server" 内の copy:css:watchpostcss:watch に変更します。
  • "postcss:watch" を記述する位置を "webpack" の上に変更します。

動作確認する(その2)

今度は npm run server コマンドを実行した後、browser-sync でブラウザに反映されるか確認します。

static/input01.html を以下のように変更します。

<head>
  ..........

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.min.css">
</head>
  • head タグ内の <link rel="stylesheet" href="/css/common.css"><link rel="stylesheet" href="/css/common.min.css"> に変更します。

npm run server コマンドを実行します。

f:id:ksby:20170730234240p:plain

ブラウザで http://localhost:9080/inquiry/input01.html にアクセスすると以下の画面が表示されます。

f:id:ksby:20170730234403p:plain

src/main/assets/css/common.cssbackground-color: snow;background-color: white; に変更すると、

f:id:ksby:20170730234558p:plain

postcss と browser-sync のメッセージが出力されて、

f:id:ksby:20170730234803p:plain

ブラウザに表示している入力画面1の背景色が white に変更されました。

f:id:ksby:20170730234913p:plain

問題なさそうです。

src/main/assets/css/common.cssbackground-color: ivory; に戻した後、Ctrl+C を押して npm run server コマンドを停止します。また static の下の input01.html 以外の html ファイルも <link rel="stylesheet" href="/css/common.min.css"> に変更します。

Autoprefixer プラグインを追加する

Autoprefixer プラグインをインストールする

npm install --save-dev autoprefixer コマンドを実行してインストールします。

f:id:ksby:20170803002524p:plain

postcss.config.js を変更する

postcss.config.js に autoprefixer の設定を追加します。

module.exports = {
    plugins: [
        require('autoprefixer')({
            browsers: [
                "last 2 versions"
            ]
        }),
        require('cssnano')({
            preset: 'default'
        })
    ]
};
  • require('autoprefixer')({ ... }) を追加します。

動作確認する

npm run server コマンドを実行します。src/main/resources/static/css/common.min.css は作り直されましたが、今のファイルだと何の prefix も追加されませんでした。

src/main/resources/static/css/common.min.cssa { display: flex; } を追加してみます。

a {
    display: flex;
}
/* body 内の背景色を白より少し色がついた色にする */
.content-wrapper {
    background-color: ivory;
}
..........

変更が検知されて、

f:id:ksby:20170803000914p:plain

src/main/resources/static/css/common.min.css を見ると vendor prefixes が自動で追加されていました。

f:id:ksby:20170803001118p:plain

stylelint プラグインを追加する

stylelint プラグインと stylelint-config-standard をインストールする

stylelint で適用するルールは stylelint-config-standard をそのまま使うことにします。npm install --save-dev stylelint stylelint-config-standard コマンドを実行してインストールします。

f:id:ksby:20170803061012p:plain f:id:ksby:20170803061142p:plain

postcss.config.js を変更する

postcss.config.js の以下の点を変更します。

module.exports = {
    plugins: [
        require('stylelint'),
        require('autoprefixer')({
            browsers: [
                "last 2 versions"
            ]
        }),
        require('cssnano')({
            preset: 'default'
        })
    ]
};
  • require('stylelint') を追加します。記述された順序でプラグインが適用されるので、一番最初に stylelint を記述します。最初はアルファベット順に一番最後に記述したら common.min.css に対して stylelint が適用されて大量にメッセージが出力されました。。。

stylelint.config.js を作成する

プロジェクトのルートディレクトリの直下に stylelint.config.js を新規作成し、以下の内容を記述します。

module.exports = {
    extends: "stylelint-config-standard"
}

動作確認する

npm run server コマンドを実行します。

common.min.css 生成時に stylelint が実行されて以下の画像のように大量のメッセージが出力されました。動作しましたが、大した行数の CSS でもないのに結構メッセージが出ますね。。。

f:id:ksby:20170803065048p:plain f:id:ksby:20170803065234p:plain

stylelint で出力されたメッセージに対応する

出力されたメッセージは以下の8種類でした。出力されたメッセージの説明は https://github.com/stylelint/stylelint/tree/master/lib/rules にあります。

  • Expected indentation of 2 spaces (indentation)
    • インデントがスペース2個でないというメッセージでした。IntelliJ IDEA だとデフォルトではスペース4個に設定されているので、stylelint.config.js に "indentation": 4 を追加して設定を変更します。
  • Expected empty line before comment (comment-empty-line-before)
    • コメントの前に1行空行がないというメッセージでした。コメントの前に1行空行を追加します。
  • Expected a leading zero (number-leading-zero)
    • 小数点以下がある数値の記述を 0.nnn ではなく .nnn のように先頭の 0 がない書き方をしていたので出力されていました。指摘された CSS は Bootstrap の css ファイルから持ってきたものなので、ここは .nnn の書き方でもメッセージが出力されないよう stylelint.config.js に "number-leading-zero": "never" を追加して設定を変更します。
  • Unexpected unit (length-zero-no-unit)
    • 値が 0 の時に 0px のように単位を記述していたので出力されているメッセージでした。単位を削除して 0px → 0 に変更します。
  • Expected single space after ":" with a single-line declaration (declaration-colon-space-after)
    • ここまでの修正でメッセージが出なくなったので対応しません。
  • Expected empty line before rule (rule-empty-line-before)
    • 定義と定義の間に空行を入れていないというメッセージでした。空行を入れずに連続して記述したい場合があるので、stylelint.config.js に "rule-empty-line-before": "never-multi-line" を追加して設定を変更します。
  • Expected indentation of 4 spaces (indentation)
    • ここまでの修正でメッセージが出なくなったので対応しません。
  • Expected empty line before at-rule (at-rule-empty-line-before)
    • .label-required を定義しているところで @media (min-width: 768px) { ... }@media (max-width: 767px) { ... } の間に空行を入れていないというメッセージでした。ここに空行は入れたくないので、stylelint.config.js に "at-rule-empty-line-before": [ "always", {"except": ["after-same-name"]} ] を追加して設定を変更します。

ここまで対応すると、追加で以下の3種類のメッセージが表示されました。

  • Expected empty line before comment (comment-empty-line-before)
    • /* ... */ のコメント行を空行を入れずに2行続けて書いていたために出ているメッセージでした。コメント行は連続で書けるようにしたいので、stylelint.config.js に "comment-empty-line-before": [ "always", {"ignore": ["after-comment"]} ] を追加して設定を変更します。
  • Expected empty line before at-rule (at-rule-empty-line-before)
    • コメント行の後に @media (min-width: 768px) { を記述しているところでメッセージが出ていました。上で追加した "at-rule-empty-line-before": [ ... ] の中に "ignore": ["after-comment"] を追加して、チェックされないようにします。
  • Unexpected empty line before rule (rule-empty-line-before)
    • .form-group .float-label の定義の前に空行が入っていたのでエラーが出ていました。ここは空行を取り除きます。

以上の対応で stylelint からメッセージが出ないようになりました。最終版の stylelint.config.js は以下のようになります。

module.exports = {
    extends: "stylelint-config-standard",
    rules: {
        "at-rule-empty-line-before": [
            "always",
            {
                "except": ["after-same-name"],
                "ignore": ["after-comment"]
            }
        ],
        "comment-empty-line-before": [
            "always",
            {
                "ignore": ["after-comment"]
            }
        ],
        "indentation": 4,
        "number-leading-zero": "never",
        "rule-empty-line-before": "never-multi-line"
    }
};

次回は。。。

Controller クラスと Thymeleaf テンプレートファイルを作成する予定です。

ソースコード

package.json

{
  "name": "boot-npm-geb-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "run-s clean:static-dir copy:all",
    "clean:static-dir": "rimraf src/main/resources/static/*",
    "copy:all": "run-p copy:bootstrap copy:admin-lte copy:font-awesome copy:ionicons",
    "copy:bootstrap": "cpx node_modules/bootstrap/dist/**/* src/main/resources/static/vendor/bootstrap",
    "copy:admin-lte": "cpx node_modules/admin-lte/dist/**/* src/main/resources/static/vendor/admin-lte",
    "copy:font-awesome": "cpx node_modules/font-awesome/{css,fonts}/**/* src/main/resources/static/vendor/font-awesome",
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons",
    "postcss:watch": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css -w --poll",
    "webpack": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync": "browser-sync",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "server": "run-p postcss:watch webpack:watch browser-sync:start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "admin-lte": "^2.3.11",
    "bootstrap": "^3.3.7",
    "font-awesome": "^4.7.0",
    "ionicons": "^3.0.0"
  },
  "devDependencies": {
    "autoprefixer": "^7.1.2",
    "browser-sync": "^2.18.13",
    "cpx": "^1.5.0",
    "cssnano": "^3.10.0",
    "npm-run-all": "^4.0.2",
    "postcss-cli": "^4.1.0",
    "rimraf": "^2.6.1",
    "stylelint": "^8.0.0",
    "stylelint-config-standard": "^17.0.0",
    "webpack": "^3.3.0"
  }
}

postcss.config.js

module.exports = {
    plugins: [
        require('stylelint'),
        require('autoprefixer')({
            browsers: [
                "last 2 versions"
            ]
        }),
        require('cssnano')({
            preset: 'default'
        })
    ]
};

stylelint.config.js

module.exports = {
    extends: "stylelint-config-standard",
    rules: {
        "at-rule-empty-line-before": [
            "always",
            {
                "except": ["after-same-name"],
                "ignore": ["after-comment"]
            }
        ],
        "comment-empty-line-before": [
            "always",
            {
                "ignore": ["after-comment"]
            }
        ],
        "indentation": 4,
        "number-leading-zero": "never",
        "rule-empty-line-before": "never-multi-line"
    }
};

common.css

/* body 内の背景色を白より少し色がついた色にする */
.content-wrapper {
    background-color: ivory;
}

/* フォーカス時の入力フィールドの色合いが adminLTE より Bootstrap のものが好みなので、そちらに定義し直す */
.form-control:focus {
    border-color: #66afe9;
    outline: 0;
    -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
}

/* form-control-static の文字列を入力項目のすぐ下に隙間をほとんど空けずに表示されるようにする */
.form-control-static {
    padding-top: 0;
    padding-bottom: 0;
    margin-bottom: -15px;
}

/* 入力チェックエラーの入力項目の背景色を薄い赤色にする */
.has-error .form-control {
    background-color: #fff5ee;
}

/* form-horizontal 内で form-control, form-control-static-inline の入力フィールドを横並びで表示する */
.form-control-inline {
    float: left;
    margin-right: 10px;
}
.form-control-static-inline {
    float: left;
    padding-top: 7px;
    margin-right: 10px;
}

/* checkbox の文字列の右側に少し隙間を空ける */
.checkbox label {
    margin-right: 10px;
}

/* 必須ラベル */
/* 767px 以下の場合には必須ラベルを右側に移動する */
@media (min-width: 768px) {
    .label-required {
        background-color: red;
        margin-right: 5px;
    }
    .form-group .float-label {
        float: right;
    }
}
@media (max-width: 767px) {
    .label-required {
        background-color: red;
        margin-left: 5px;
    }
}

/* 画面下のボタンを大きく表示させる */
.btn {
    width: 150px;
}

履歴

2017/08/05 初版発行。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その10 )( 各画面の HTML を作成する3 )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その9 )( 各画面の HTML を作成する2 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 各画面の HTML を作成します。
    • 今回は確認画面、完了画面です。

参照したサイト・書籍

目次

  1. 確認画面の HTML を作成する
  2. 完了画面の HTML を作成する

手順

確認画面の HTML を作成する

static/inquiry/confirm.html を新規作成します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>入力フォーム - 確認画面</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.css">
  <style>
    /* セルの上部の罫線を表示しないようようにし、セル内の余白を詰める */
    .table > tbody> tr > th,
    .table > tbody> tr > td {
      border-top: none;
      padding: 5px;
    }
  </style>
</head>

<body class="skin-blue layout-top-nav">
<div class="wrapper">

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        確認画面
      </h1>
    </section>

    <!-- Main content -->
    <section class="content">
      <div class="row">
        <div class="col-xs-12">
          <form id="confirmForm" method="post" action="">
            <table class="table">
              <colgroup>
                <col width="15%"/>
                <col width="85%"/>
              </colgroup>

              <!-- 入力画面1の項目 -->
              <tr>
                <th nowrap>お名前(漢字)</th>
                <td>田中 太郎</td>
              </tr>
              <tr>
                <th nowrap>お名前(かな)</th>
                <td>たなか たろう</td>
              </tr>
              <tr>
                <th nowrap>性別</th>
                <td>男性</td>
              </tr>
              <tr>
                <th nowrap>年齢</th>
                <td>30歳</td>
              </tr>
              <tr>
                <th nowrap>職業</th>
                <td>会社員</td>
              </tr>
              <tr>
                <td colspan="2"><button class="btn bg-blue js-btn-input01"><i class="fa fa-arrow-left"></i> 修正する</button></td>
              </tr>

              <!-- 入力画面2の項目 -->
              <tr>
                <th nowrap>郵便番号</th>
                <td>〒102-0072</td>
              </tr>
              <tr>
                <th nowrap>住所</th>
                <td>東京都千代田区飯田橋1-1</td>
              </tr>
              <tr>
                <th nowrap>電話番号</th>
                <td>03-1234-5678</td>
              </tr>
              <tr>
                <th nowrap>メールアドレス</th>
                <td>taro.tanaka@sample.co.jp</td>
              </tr>
              <tr>
                <th nowrap>職業</th>
                <td>会社員</td>
              </tr>
              <tr>
                <td colspan="2"><button class="btn bg-blue js-btn-input02"><i class="fa fa-arrow-left"></i> 修正する</button></td>
              </tr>

              <!-- 入力画面3の項目 -->
              <tr>
                <th nowrap>お問い合わせの種類1</th>
                <td>製品に関するお問い合わせ</td>
              </tr>
              <tr>
                <th nowrap>お問い合わせの種類2</th>
                <td>見積が欲しい</td>
              </tr>
              <tr>
                <th nowrap>お問い合わせの内容</th>
                <td>
                  ここに、<br/>
                  入力されたお問い合わせの内容が表示されます。
                </td>
              </tr>
              <tr>
                <th nowrap>アンケート</th>
                <td>
                  <ul style="padding-left: 20px">
                    <li>選択肢1だけ長くしてみる</li>
                    <li>選択肢5が少し長い</li>
                    <li></li>
                  </ul>
                </td>
              </tr>
              <tr>
                <td colspan="2"><button class="btn bg-blue js-btn-input03"><i class="fa fa-arrow-left"></i> 修正する</button></td>
              </tr>
            </table>

            <div class="text-center">
              <button class="btn bg-green js-btn-send"><i class="fa fa-arrow-right"></i> 送信する</button>
            </div>
          </form>
        </div>
      </div>
    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->
<script src="/js/inquiry/confirm.js"></script>

</body>
</html>

画面の初期表示は以下のようになります(IE のキャプチャツールがうまく動かなかったため、今回は Chrome でキャプチャしています)。

f:id:ksby:20170730104232p:plain f:id:ksby:20170730104332p:plain

完了画面の HTML を作成する

static/inquiry/complete.html を新規作成します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>入力フォーム - 完了画面</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.css">
</head>

<body class="skin-blue layout-top-nav">
<div class="wrapper">

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        完了画面
      </h1>
    </section>

    <!-- Main content -->
    <section class="content">
      <div class="row">
        <div class="col-xs-12">
          <div class="text-center">
            <p>お問い合わせありがとうございました。</p>
            <a href="/inquiry/input01.html"><button class="btn bg-green"><i class="fa fa-reply"></i> 入力画面へ</button></a>
          </div>
        </div>
      </div>
    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->
<script src="/js/inquiry/complete.js"></script>

</body>
</html>

画面の初期表示は以下のようになります。

f:id:ksby:20170730104512p:plain f:id:ksby:20170730104606p:plain

履歴

2017/07/30
初版発行。
2017/08/11
* 「修正する」ボタンのセレクタを全て js-btn-input01 にしていたので、js-btn-input02, js-btn-input03 に修正した。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その9 )( 各画面の HTML を作成する2 )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その8 )( 各画面の HTML を作成する ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 各画面の HTML を作成します。
    • 今回は入力画面2、入力画面3です。

参照したサイト・書籍

目次

  1. 入力画面2の HTML を作成する
  2. 入力画面3の HTML を作成する

手順

入力画面2の HTML を作成する

static/inquiry/input02.html を新規作成します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>入力フォーム - 入力画面2</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.css">
</head>

<body class="skin-blue layout-top-nav">
<div class="wrapper">

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        入力画面2
      </h1>
    </section>

    <!-- Main content -->
    <section class="content">
      <div class="row">
        <div class="col-xs-12">
          <form id="input02Form" class="form-horizontal" method="post" action="">
            <!-- 郵便番号 -->
            <div class="form-group" id="form-group-zipcode">
              <div class="control-label col-sm-2">
                <label class="float-label">郵便番号</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <p class="form-control-static-inline"></p>
                  <input type="text" name="zipcode1" id="zipcode1" class="form-control form-control-inline" style="width: 60px;" value="" placeholder=""/>
                  <p class="form-control-static-inline">-</p>
                  <input type="text" name="zipcode2" id="zipcode2" class="form-control form-control-inline" style="width: 80px;" value="" placeholder=""/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- 住所 -->
            <div class="form-group" id="form-group-address">
              <div class="control-label col-sm-2">
                <label class="float-label">住所</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="address" id="address" class="form-control" value="" placeholder="例)東京都千代田区飯田橋1-1"/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- 電話番号 -->
            <div class="form-group" id="form-group-tel">
              <div class="control-label col-sm-2">
                <label class="float-label">電話番号</label>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="tel1" id="tel1" class="form-control form-control-inline" style="width: 100px;" value="" placeholder=""/>
                  <p class="form-control-static-inline">-</p>
                  <input type="text" name="tel2" id="tel2" class="form-control form-control-inline" style="width: 100px;" value="" placeholder=""/>
                  <p class="form-control-static-inline">-</p>
                  <input type="text" name="tel3" id="tel3" class="form-control form-control-inline" style="width: 100px;" value="" placeholder=""/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- メールアドレス -->
            <div class="form-group" id="form-group-email">
              <div class="control-label col-sm-2">
                <label class="float-label">メールアドレス</label>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="email" id="email" class="form-control" style="width: 250px;" value="" placeholder="例)taro.tanaka@sample.co.jp"/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <div class="text-center">
              <br/><p class="text-primary text-bold">※電話番号とメールアドレスのいずれか一方は必ず入力してください。</p><br/>
              <button class="btn bg-blue js-btn-back"><i class="fa fa-arrow-left"></i> 前の画面へ戻る</button>
              <button class="btn bg-green js-btn-next" disabled><i class="fa fa-arrow-right"></i> 次へ</button>
            </div>
          </form>
        </div>
      </div>
    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->
<script src="/js/inquiry/input02.js"></script>

</body>
</html>

画面の初期表示は以下のようになります。

f:id:ksby:20170729173851p:plain f:id:ksby:20170729231148p:plain

入力画面3の HTML を作成する

static/inquiry/input03.html を新規作成します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>入力フォーム - 入力画面3</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.css">
  <style>
    /* 「チェックボックス複数行」の入力項目のチェックボックスを複数行に書くので、 */
    /* 異なる行のチェックボックスの位置を左揃えにする                         */
    @media (min-width: 768px) {
      #multiline-checkbox .checkbox label {
        display: block;
        float: left;
        width: 180px;
      }
    }
    @media (max-width: 767px) {
      #multiline-checkbox .checkbox label {
        display: block;
        float: left;
        width: 100%;
      }
    }
  </style>
</head>

<body class="skin-blue layout-top-nav">
<div class="wrapper">

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        入力画面3
      </h1>
    </section>

    <!-- Main content -->
    <section class="content">
      <div class="row">
        <div class="col-xs-12">
          <form id="input03Form" class="form-horizontal" method="post" action="">
            <!-- お問い合わせの種類1 -->
            <div class="form-group" id="form-group-type1">
              <div class="control-label col-sm-2">
                <label class="float-label">お問い合わせの種類1</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <select name="type1" id="type1" class="form-control" style="width: 250px;">
                    <option value="">選択してください</option>
                    <option value="1">製品に関するお問い合わせ</option>
                    <option value="2">人事・採用情報</option>
                    <option value="3">その他</option>
                  </select>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- お問い合わせの種類2 -->
            <div class="form-group" id="form-group-type2">
              <div class="control-label col-sm-2">
                <label class="float-label">お問い合わせの種類2</label>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <div class="checkbox">
                    <label><input type="checkbox" name="type2">見積が欲しい</label>
                    <label><input type="checkbox" name="type2">資料が欲しい</label>
                    <label><input type="checkbox" name="type2">その他の問い合わせ</label>
                  </div>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- お問い合わせの内容 -->
            <div class="form-group" id="form-group-inquiry">
              <div class="control-label col-sm-2">
                <label class="float-label">お問い合わせの内容</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <textarea rows="5" name="inquiry" id="inquiry" class="form-control" placeholder="お問い合わせ内容を入力して下さい"></textarea>
                </div></div>
                <div class="row"><div class="col-sm-10"><p class="form-control-static"><small>※最大500文字</small></p></div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <div class="form-group" id="form-group-survey">
              <div class="control-label col-sm-2">
                <label class="float-label">アンケート</label>
              </div>
              <div class="col-sm-10" id="multiline-checkbox">
                <div class="row"><div class="col-sm-12">
                  <div class="checkbox">
                    <label><input type="checkbox" name="survey" value="1">選択肢1だけ長くしてみる</label>
                    <label><input type="checkbox" name="survey" value="2">選択肢2</label>
                    <label><input type="checkbox" name="survey" value="3">選択肢3</label>
                  </div>
                </div></div>
                <div class="row"><div class="col-sm-12">
                  <div class="checkbox">
                    <label><input type="checkbox" name="survey" value="4">選択肢4</label>
                    <label><input type="checkbox" name="survey" value="5">選択肢5が少し長い</label>
                    <label><input type="checkbox" name="survey" value="6">選択肢6</label>
                  </div>
                </div></div>
                <div class="row"><div class="col-sm-12">
                  <div class="checkbox">
                    <label><input type="checkbox" name="survey" value="7">選択肢7</label>
                    <label><input type="checkbox" name="survey" value="8">選択肢8</label>
                    <label><input type="checkbox" name="survey" value="9"></label>
                  </div>
                </div></div>
              </div>
            </div>

            <div class="text-center">
              <button class="btn bg-blue js-btn-back"><i class="fa fa-arrow-left"></i> 前の画面へ戻る</button>
              <button class="btn bg-green js-btn-confirm" disabled><i class="fa fa-arrow-right"></i> 確認画面へ</button>
            </div>
          </form>
        </div>
      </div>
    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->
<script src="/js/inquiry/input03.js"></script>

</body>
</html>

画面の初期表示は以下のようになります。

f:id:ksby:20170729230101p:plain f:id:ksby:20170729231008p:plain

履歴

2017/07/29
初版発行。
2017/08/08
* 入力画面2の form タグの id を input01Forminput02Form へ変更しました。
* 入力画面3の form タグの id を input01Forminput03Form へ変更しました。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その8 )( 各画面の HTML を作成する )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その7 )( webpack + browser-sync をインストールする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 各画面の HTML を作成します。
    • 今回は入力画面1だけです。2~3回に分ける予定です。

参照したサイト・書籍

  1. Bootstrap3でFormを簡単に作る方法
    http://tetra-themes.com/bootstrap3-form-197/

  2. Bootstrap3日本語リファレンス - CSS - フォーム - サポートしているフォーム部品
    http://bootstrap3.cyberlab.info/css/forms-supportedControls.html

  3. Form inline inside a form horizontal in twitter bootstrap?
    https://stackoverflow.com/questions/12201835/form-inline-inside-a-form-horizontal-in-twitter-bootstrap

  4. リキッド・レイアウトでチェックボックスを綺麗に並べる
    http://webos-goodies.jp/archives/51275332.html

  5. npm-scripts で作るフロントエンド環境の構築
    http://ceblog.mediba.jp/post/154522336717/npm-scripts

目次

  1. 画面の仕様を決める
  2. starter.html からテンプレートに使用する template.html を作成する
  3. src/main/assets/css の下で更新された css ファイルを自動で src/main/resources/static/css の下に反映する仕組みを作成する
  4. 画面共通で使用する css ファイルを作成する
  5. 入力画面1の HTML を作成する

手順

画面の仕様を決める

「Spring Boot + npm + Geb で入力フォームを作ってテストする」は Geb でのテストのサンプルを作成することが目的なので、いろいろ要素を盛り込みます。

  • 入力項目には以下の要素を入れます。
    • テキストボックス(<input type="text" ..."
    • ラジオボタン<input type="radio" ..."
    • チェックボックス<input type="checkbox" ..."
    • テキストエリア(<input type="textarea" ..."
  • 画面の入力チェックは Javascript で行います。入力チェックエラー時にはラベルと入力フィールドを赤く表示し、入力項目の下にエラーメッセージが表示されるようにします。
  • Ajax でデータを送受信する仕組みも入れます。「郵便番号」を入力したら「住所」に都道府県・市区町村・町丁目名がセットされるようにします。郵便番号検索API を利用する想定です。

starter.html からテンプレートに使用する template.html を作成する

作業開始前にコマンドプロンプトnpm run server コマンドを実行して browser-sync を起動しておきます。

node_modules/admin-lte/starter.html を static の下にコピーし、ファイル名を template.html に変更した後、以下の点を変更します。

  • header タグ内の以下の部分を変更します。
    • <title>AdminLTE 2 | Starter</title><title>入力フォーム</title>
    • <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"><link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
    • <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"><link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
    • <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css"><link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
    • <link rel="stylesheet" href="dist/css/AdminLTE.min.css"><link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
    • <link rel="stylesheet" href="dist/css/skins/skin-blue.min.css"><link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">
    • 以下の2行は削除します。
      • <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      • <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
  • <body class="skin-blue layout-top-nav"><body class="skin-blue layout-top-nav"> に変更します。
  • body タグ内の以下の部分は削除します。
    • <!-- Main Header --><header class="main-header"> ... </header> の部分
    • <!-- Left side column. contains the logo and sidebar --><aside class="main-sidebar"> ... </aside> の部分
    • <!-- Main Footer --><footer class="main-footer"> ... </footer> の部分
    • <!-- Control Sidebar --><aside class="control-sidebar control-sidebar-dark"> ... </aside> の部分
    • <!-- Add the sidebar's background. This div must be placed immediately after the control sidebar --><div class="control-sidebar-bg"></div> の部分
    • js ファイルを読み込んでいる以下の3行。
      • <script src="plugins/jQuery/jquery-2.2.3.min.js"></script>
      • <script src="bootstrap/js/bootstrap.min.js"></script>
      • <script src="dist/js/app.min.js"></script>
  • <section class="content-header"> ... </section> 内の以下の部分を削除します。
    • <small>Optional description</small>
    • <ol class="breadcrumb"> ... </ol> の部分
  • あとはコメントで残す必要がないものも全て削除します。

ここまでの変更で template.html は以下のようになります。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>入力フォーム</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">
</head>

<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        Page Header
      </h1>
    </section>

    <!-- Main content -->
    <section class="content">

      <!-- Your Page Content Here -->

    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->

</body>
</html>

http://localhost:9080/template.html にアクセスして画面を表示すると以下の画像のようになります。

f:id:ksby:20170726233139p:plain

Ctrl+C を押して npm run server コマンドを一旦停止します。

src/main/assets/css の下で更新された css ファイルを自動で src/main/resources/static/css の下に反映する仕組みを作成する

src/main/assets/css の下に common.css を新規作成し、変更されたら自動で src/main/resources/static/css の下にコピーする仕組みを作成します。変更の検知は cpx の watch 機能を利用します。

src/main/assets の下に css ディレクトリを新規作成し、その下に common.css を新規作成します。この時点では common.css の中身は空です。

f:id:ksby:20170727012330p:plain

package.json の scripts を以下のように変更します。

  "scripts": {
    ..........,
    "copy:css:watch": "cpx src/main/assets/css/**/* src/main/resources/static/css -w",
    "server": "run-p copy:css:watch webpack:watch browser-sync:start"
  },
  • "copy:css:watch": "cpx src/main/assets/css/**/* src/main/resources/static/css -w" を追加します。
  • "server"copy:css:watch を追加します。

static/template.html を以下のように変更します。

<head>
  ..........

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.css">
</head>
  • <link rel="stylesheet" href="/css/common.css"> を追加し、src/main/resources/static/css/common.css を読み込むようにします。

以上で準備は完了です。common.css を変更したら自動でブラウザに反映されるか確認します。

npm run server コマンドを実行します。

f:id:ksby:20170727013247p:plain f:id:ksby:20170727013350p:plain

src/main/resources/static の下に css/common.css がコピーされます。

f:id:ksby:20170727013931p:plain

http://localhost:9080/template.html にアクセスすると以下の画面が表示されます。

f:id:ksby:20170727013518p:plain

src/main/assets/css/common.css に背景色を変更する以下の記述を追加します。

/* body 内の背景色を白より少し色がついた色にする */
.content-wrapper {
    background-color: ivory;
}

browser-sync が common.css の変更を検知したことを示すログを出力し、

f:id:ksby:20170727014127p:plain

ブラウザに表示している画面の背景色も手動リロードすることなく変わりました。

f:id:ksby:20170727014229p:plain

画面共通で使用する css ファイルを作成する

src/main/assets/css/common.css に以下の内容を記述します。

/* body 内の背景色を白より少し色がついた色にする */
.content-wrapper {
    background-color: ivory;
}
/* フォーカス時の入力フィールドの色合いが adminLTE より Bootstrap のものが好みなので、そちらに定義し直す */
.form-control:focus {
    border-color: #66afe9;
    outline: 0;
    -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
}
/* form-control-static の文字列を入力項目のすぐ下に隙間をほとんど空けずに表示されるようにする */
.form-control-static {
    padding-top: 0px;
    padding-bottom: 0px;
    margin-bottom: -15px;
}
/* 入力チェックエラーの入力項目の背景色を薄い赤色にする */
.has-error .form-control {
    background-color: #fff5ee;
}
/* form-horizontal 内で form-control, form-control-static-inline の入力フィールドを横並びで表示する */
.form-control-inline {
    float:left;
    margin-right: 10px;
}
.form-control-static-inline {
    float: left;
    padding-top: 7px;
    margin-right: 10px;
}
/* checkbox の文字列の右側に少し隙間を空ける */
.checkbox label {
    margin-right: 10px;
}
/* 必須ラベル */
/* 767px 以下の場合には必須ラベルを右側に移動する */
@media (min-width: 768px) {
    .label-required {
        background-color: red;
        margin-right: 5px;
    }
    .form-group .float-label {
        float: right;
    }
}
@media (max-width: 767px) {
    .label-required {
        background-color: red;
        margin-left: 5px;
    }
}
/* 画面下のボタンを大きく表示させる */
.btn {
    width: 150px;
}

入力画面1の HTML を作成する

static の下に inquiry ディレクトリを作成した後、static/template.html をコピーして static/inquiry/input01.html を作成します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>入力フォーム - 入力画面1</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.css">
</head>

<body class="skin-blue layout-top-nav">
<div class="wrapper">

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        入力画面1
      </h1>
    </section>

    <!-- Main content -->
    <section class="content">
      <div class="row">
        <div class="col-xs-12">
          <form id="input01Form" class="form-horizontal" method="post" action="">
            <!-- お名前(漢字) -->
            <div class="form-group" id="form-group-name">
              <div class="control-label col-sm-2">
                <label class="float-label">お名前(漢字)</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="lastname" id="lastname" class="form-control form-control-inline" style="width: 150px;" value="" placeholder="例)田中"/>
                  <input type="text" name="firstname" id="firstname" class="form-control form-control-inline" style="width: 150px;" value="" placeholder="例)太郎"/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- お名前(かな) -->
            <div class="form-group" id="form-group-kana">
              <div class="control-label col-sm-2">
                <label class="float-label">お名前(かな)</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="lastkana" id="lastkana" class="form-control form-control-inline" style="width: 150px;" value="" placeholder="例)たなか"/>
                  <input type="text" name="firstkana" id="firstkana" class="form-control form-control-inline" style="width: 150px;" value="" placeholder="例)たろう"/>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- 性別 -->
            <div class="form-group" id="form-group-sex">
              <div class="control-label col-sm-2">
                <label class="float-label">性別</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <div class="radio-inline"><label><input type="radio" name="sex" value="1">男性</label></div>
                  <div class="radio-inline"><label><input type="radio" name="sex" value="2">女性</label></div>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- 年齢 -->
            <div class="form-group" id="form-group-age">
              <div class="control-label col-sm-2">
                <label class="float-label">年齢</label>
                <div class="label label-required">必須</div>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <input type="text" name="age" id="age" class="form-control form-control-inline" style="width: 150px;" value="" placeholder="例)30"/>
                  <p class="form-control-static-inline"></p>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <!-- 職業 -->
            <div class="form-group" id="form-group-job">
              <div class="control-label col-sm-2">
                <label class="float-label">職業</label>
              </div>
              <div class="col-sm-10">
                <div class="row"><div class="col-sm-10">
                  <select name="job" id="job" class="form-control" style="width: 250px;">
                    <option value="">選択してください</option>
                    <option value="1">会社員</option>
                    <option value="2">学生</option>
                    <option value="3">その他</option>
                  </select>
                </div></div>
                <div class="row hidden js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>

            <div class="text-center">
              <button class="btn bg-green js-btn-next" disabled><i class="fa fa-arrow-right"></i> 次へ</button>
            </div>
          </form>
        </div>
      </div>
    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->
<script src="/js/inquiry/input01.js"></script>

</body>
</html>

画面の初期表示は以下のようになります。

f:id:ksby:20170729090551p:plain f:id:ksby:20170729091805p:plain

画面上の入力項目を全て入力し、入力チェックでエラーが出なかった場合は以下のようになります。

f:id:ksby:20170729090905p:plain

            <!-- 職業 -->
            <div class="form-group has-success" id="form-group-job">
              ..........
            </div>

            <div class="text-center">
              <button class="btn bg-green js-btn-next"><i class="fa fa-arrow-right"></i> 次へ</button>
            </div>
  • form-group に has-success を追加して <div class="form-group" ...><div class="form-group has-success" ...> にします。
  • disabled を削除して <button class="btn bg-green js-btn-next" disabled><button class="btn bg-green js-btn-next"> にします。

入力チェックがエラーだった場合は以下のようになります。

f:id:ksby:20170729092216p:plain

            <!-- 職業 -->
            <div class="form-group has-error" id="form-group-job">
              ..........
              <div class="col-sm-10">
                ..........
                <div class="row js-errmsg"><div class="col-sm-10"><p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p></div></div>
              </div>
            </div>
  • form-group に has-error を追加して <div class="form-group" ...><div class="form-group has-error" ...> にします。
  • エラーメッセージの表示部分から hidden を削除して <div class="row hidden js-errmsg"><div class="row js-errmsg"> にします。

今回 HTML を作成して思いましたが browser-sync が本当に便利です。手動でリロードをしなくてよいのがこんなに便利だとは。もっと早く知りたかった。。。

履歴

2017/07/29
初版発行。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その7 )( webpack + browser-sync をインストールする )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その6 )( URL の決定 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 各画面の HTML を作成する前に、今回は HTML の確認を Tomcat ではなく webpack + browser-sync を使用して行ってみようと思います。
    • まずはインストールして試してみます。
  • 「Spring Boot + npm + Geb で入力フォームを作ってテストする」では webpack は js ファイルの開発にしか使わないので HTML の確認だけなら browser-sync だけでいいのですが、両方インストールして変更したファイル(html ファイルだけでなく js ファイルも)を watch する仕組みを構築します。

参照したサイト・書籍

  1. 最新版で学ぶwebpack 3入門 – JavaScript開発で人気のバンドルツール
    https://ics.media/entry/12140

  2. Building Static Sites with Webpack
    https://blog.binoy.io/building-static-sites-with-webpack-4fc489ceabca

  3. WebPack-Dev-ServerからBrowsersyncに乗り換えた
    https://saku.io/move-from-webpack-dev-server-to-browser-sync/

  4. webpackで複数のディレクトリへ出力する
    http://webdesign-dackel.com/2015/09/10/webpack-multiple-output/

  5. window.onloadとjQueryの$(document).ready等の比較
    https://rcmdnk.com/blog/2015/07/11/computer-javascript-jquery/

目次

  1. webpack をインストールする
  2. browser-sync をインストールする
  3. webpack + browser-sync 起動用の npm-scripts を作成する
  4. 動作確認する

手順

webpack をインストールする

npm install --save-dev webpack コマンドを実行して webpack をインストールします。

f:id:ksby:20170724061949p:plain f:id:ksby:20170724062146p:plain f:id:ksby:20170724062319p:plain f:id:ksby:20170724062428p:plain f:id:ksby:20170724062534p:plain

プロジェクトのルートディレクトリ直下に webpack.config.js というファイルを新規作成し、以下の内容を記述します。

module.exports = {
    entry: {
        "js/app": ["./src/main/assets/js/app.js"]
    },
    output: {
        path: __dirname + "/src/main/resources/static",
        publicPath: "/",
        filename: "[name].js"
    }
};
  • js ファイルは src/main の下に assets/js ディレクトリを作成し、その下に作成します。
  • 今回は動作確認のために app.js を作成します。
  • webpack により処理・生成された js ファイルは src/main/resources/static/js の下に配置します。

src/main の下に assets/js ディレクトリを作成し、src/main/assets/js の下に app.js を作成します。

f:id:ksby:20170724063549p:plain

app.js には以下の内容を記述します。

var $ = require("admin-lte/plugins/jQuery/jquery-2.2.3.min.js");

webpack を起動しやすくするために package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "webpack": "webpack"
  },

動作確認します。まずは npm run webpack -- --version コマンドを実行してバージョン番号が表示されることを確認します。

f:id:ksby:20170724064207p:plain

3.3.0 と表示されました。

次に npm run webpack コマンドを実行し、src/main/assets/js/app.js から src/main/resources/static/js/app.js が生成されることを確認します。

f:id:ksby:20170724064532p:plain

コマンドが正常に終了し、src/main/resources/static/js の下に app.js が生成されています。

f:id:ksby:20170724064722p:plain

app.js を開くと以下の内容で始まるファイルでした。

f:id:ksby:20170724064903p:plain

問題なく動作しているようですので、次へ進みます。

browser-sync をインストールする

npm install --save-dev browser-sync コマンドを実行して browser-sync をインストールします。

f:id:ksby:20170724065807p:plain f:id:ksby:20170724065903p:plain f:id:ksby:20170724070002p:plain f:id:ksby:20170724070103p:plain f:id:ksby:20170724070208p:plain f:id:ksby:20170724070311p:plain

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "browser-sync": "browser-sync"
  },

npm run browser-sync -- init コマンドを実行して bs-config.js を生成します。

f:id:ksby:20170724070615p:plain

bs-config.js の以下の点を変更します。

  • "weinre": { "port": 8080 }"weinre": { "port": 9081 } に変更します。
  • "files": false"files": [ "./static/**/*", "./src/main/resources/static/**/*" ] に変更します。
  • "server": false"server": { "baseDir": [ "./static", "./src/main/resources/static" ] }に変更します。
  • "port": 3000"port": 9080 に変更します。
  • "notify": true"notify": false に変更します。
/*
 |--------------------------------------------------------------------------
 | Browser-sync config file
 |--------------------------------------------------------------------------
 |
 | For up-to-date information about the options:
 |   http://www.browsersync.io/docs/options/
 |
 | There are more options than you see here, these are just the ones that are
 | set internally. See the website for more info.
 |
 |
 */
module.exports = {
    "ui": {
        "port": 3001,
        "weinre": {
            "port": 9081
        }
    },
    "files": [
        "./static/**/*",
        "./src/main/resources/static/**/*"
    ],
    "watchEvents": [
        "change"
    ],
    "watchOptions": {
        "ignoreInitial": true
    },
    "server": {
        "baseDir": [
            "./static",
            "./src/main/resources/static"
        ]
    },
    "proxy": false,
    "port": 9080,
    "middleware": false,
    "serveStatic": [],
    "ghostMode": {
        "clicks": true,
        "scroll": true,
        "location": true,
        "forms": {
            "submit": true,
            "inputs": true,
            "toggles": true
        }
    },
    "logLevel": "info",
    "logPrefix": "Browsersync",
    "logConnections": false,
    "logFileChanges": true,
    "logSnippet": true,
    "rewriteRules": [],
    "open": "local",
    "browser": "default",
    "cors": false,
    "xip": false,
    "hostnameSuffix": false,
    "reloadOnRestart": false,
    "notify": false,
    "scrollProportionally": true,
    "scrollThrottle": 0,
    "scrollRestoreTechnique": "window.name",
    "scrollElements": [],
    "scrollElementMapping": [],
    "reloadDelay": 0,
    "reloadDebounce": 0,
    "reloadThrottle": 0,
    "plugins": [],
    "injectChanges": true,
    "startPath": null,
    "minify": true,
    "host": null,
    "localOnly": false,
    "codeSync": true,
    "timestamps": true,
    "clientEvents": [
        "scroll",
        "scroll:element",
        "input:text",
        "input:toggles",
        "form:submit",
        "form:reset",
        "click"
    ],
    "socket": {
        "socketIoOptions": {
            "log": false
        },
        "socketIoClientConfig": {
            "reconnectionAttempts": 50
        },
        "path": "/browser-sync/socket.io",
        "clientPath": "/browser-sync",
        "namespace": "/browser-sync",
        "clients": {
            "heartbeatTimeout": 5000
        }
    },
    "tagNames": {
        "less": "link",
        "scss": "link",
        "css": "link",
        "jpg": "img",
        "jpeg": "img",
        "png": "img",
        "svg": "img",
        "gif": "img",
        "js": "script"
    }
};

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "browser-sync:start": "browser-sync start --config bs-config.js"
  },

プロジェクトのルートディレクトリ直下に static ディレクトリを作成します。今回は Thymeleaf テンプレート前の html ファイルは jar ファイルに入れないようにするために、src/main/resources/static ではなく static の下に作成することにします。

npm run browser-sync:start コマンドを実行してみます。

f:id:ksby:20170724072032p:plain

特にエラーは出ずに起動しているようです。http://localhost:3001 にアクセスすると browser-sync の ui が表示されました。

f:id:ksby:20170724072148p:plain

npm run browser-sync:start コマンドを実行したコマンドプロンプトで Ctrl+C を押して終了すると、ブラウザに以下の画面が表示されました。サーバと同期して動いているようです。

f:id:ksby:20170724072416p:plain

問題なさそうですので、次へ進みます。

webpack + browser-sync 起動用の npm-scripts を作成する

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........,
    "webpack": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync": "browser-sync",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "server": "run-p webpack:watch browser-sync:start"
  },
  • "webpack:watch": "webpack --watch" を追加します。
  • "server": "run-p webpack:watch browser-sync:start" を追加します。

npm run server コマンドで webpack と browser-sync が起動して、ファイルが変更されるとブラウザに自動反映されるようにします。

動作確認する

static の下に以下の内容の sample.html を作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
テスト用ページです。<br/>
<span id="sample">xxx</span>
<script src="/js/app.js"></script>
</body>
</html>

src/main/assets/js/app.js を以下の内容に変更します。

var $ = require("admin-lte/plugins/jQuery/jquery-2.2.3.min.js");

$(document).ready(function() {
    $("#sample").text("サンプル");
});

npm run server コマンドを実行します。

f:id:ksby:20170724213650p:plain

ブラウザが起動しますので、http://localhost:9080/sample.html にアクセスしてみます。

f:id:ksby:20170724213858p:plain

sample.html に記述した文字列が表示されており、また app.js に記述した処理も反映されていました。

sample.html を以下の内容に変更してみます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
サンプルですよ?<br/>
<span id="sample">xxx</span>
<script src="/js/app.js"></script>
</body>
</html>

ブラウザを見るとリロードすることなく反映されています。

f:id:ksby:20170724214205p:plain

今度は app.js を以下の内容に修正してみます。

var $ = require("admin-lte/plugins/jQuery/jquery-2.2.3.min.js");

$(document).ready(function() {
    $("#sample").text("app.js を修正してみました");
});

webpack の処理が実行されて src/main/resources/static/js/app.js が作り直されて、

f:id:ksby:20170724214540p:plain

ブラウザの方に手動でリロードすることなく反映されました。

f:id:ksby:20170724214748p:plain

他にも browser-sync はこんなことができるようです。

  • 複数のブラウザ(下の画像では IEChrome)で同じページを見ている時に、ファイルが変更されると全てのブラウザに手動リロードすることなく反映されます。

    f:id:ksby:20170724223414p:plain

  • 複数のブラウザで同じフォームを見ている時に、片方のブラウザ(IE)に入力すると、自動で別のブラウザ(Chrome)にも入力されます。

    f:id:ksby:20170724223724p:plain

  • sample2.html を作成します。その後で sample.html には sample.html → sample2.html へ遷移するリンクを、sample2.html には sample2.html → sample.html へ遷移するリンクを記述してから IEChrome で sample.html を表示した後

    f:id:ksby:20170724224224p:plain

    IE の方のリンクをクリックして sample2.html へ遷移すると、Chrome の方でも sample2.html へ遷移します。

    f:id:ksby:20170724224858p:plain

browser-sync ってすごい面白い! まさか、こんな便利なソフトがあるとは。

他にも browser-sync には proxy の機能もあるようで、Spring Boot の Tomcat と連携して自動リロードもできるのでしょうか。。。? 入力フォームができたら試してみたいと思います。

Ctrl+C を押して webpack と browser-sync を停止します。今回作成した sample.html, sample2.html は commit せずに削除します。

最後に package.json, webpack.config.js, bs-config.js を記載しておきます。

ソースコード

package.json

{
  "name": "boot-npm-geb-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "run-s clean:static-dir copy:all",
    "clean:static-dir": "rimraf src/main/resources/static/*",
    "copy:all": "run-p copy:bootstrap copy:admin-lte copy:font-awesome copy:ionicons",
    "copy:bootstrap": "cpx node_modules/bootstrap/dist/**/* src/main/resources/static/vendor/bootstrap",
    "copy:admin-lte": "cpx node_modules/admin-lte/dist/**/* src/main/resources/static/vendor/admin-lte",
    "copy:font-awesome": "cpx node_modules/font-awesome/{css,fonts}/**/* src/main/resources/static/vendor/font-awesome",
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons",
    "webpack": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync": "browser-sync",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "server": "run-p webpack:watch browser-sync:start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "admin-lte": "^2.3.11",
    "bootstrap": "^3.3.7",
    "font-awesome": "^4.7.0",
    "ionicons": "^3.0.0"
  },
  "devDependencies": {
    "browser-sync": "^2.18.13",
    "cpx": "^1.5.0",
    "npm-run-all": "^4.0.2",
    "rimraf": "^2.6.1",
    "webpack": "^3.3.0"
  }
}

webpack.config.js

module.exports = {
    entry: {
        "js/app": ["./src/main/assets/js/app.js"]
    },
    output: {
        path: __dirname + "/src/main/resources/static",
        publicPath: "/",
        filename: "[name].js"
    }
};

bs-config.js

/*
 |--------------------------------------------------------------------------
 | Browser-sync config file
 |--------------------------------------------------------------------------
 |
 | For up-to-date information about the options:
 |   http://www.browsersync.io/docs/options/
 |
 | There are more options than you see here, these are just the ones that are
 | set internally. See the website for more info.
 |
 |
 */
module.exports = {
    "ui": {
        "port": 3001,
        "weinre": {
            "port": 9081
        }
    },
    "files": [
        "./static/**/*",
        "./src/main/resources/static/**/*"
    ],
    "watchEvents": [
        "change"
    ],
    "watchOptions": {
        "ignoreInitial": true
    },
    "server": {
        "baseDir": [
            "./static",
            "./src/main/resources/static"
        ]
    },
    "proxy": false,
    "port": 9080,
    "middleware": false,
    "serveStatic": [],
    "ghostMode": {
        "clicks": true,
        "scroll": true,
        "location": true,
        "forms": {
            "submit": true,
            "inputs": true,
            "toggles": true
        }
    },
    "logLevel": "info",
    "logPrefix": "Browsersync",
    "logConnections": false,
    "logFileChanges": true,
    "logSnippet": true,
    "rewriteRules": [],
    "open": "local",
    "browser": "default",
    "cors": false,
    "xip": false,
    "hostnameSuffix": false,
    "reloadOnRestart": false,
    "notify": false,
    "scrollProportionally": true,
    "scrollThrottle": 0,
    "scrollRestoreTechnique": "window.name",
    "scrollElements": [],
    "scrollElementMapping": [],
    "reloadDelay": 0,
    "reloadDebounce": 0,
    "reloadThrottle": 0,
    "plugins": [],
    "injectChanges": true,
    "startPath": null,
    "minify": true,
    "host": null,
    "localOnly": false,
    "codeSync": true,
    "timestamps": true,
    "clientEvents": [
        "scroll",
        "scroll:element",
        "input:text",
        "input:toggles",
        "form:submit",
        "form:reset",
        "click"
    ],
    "socket": {
        "socketIoOptions": {
            "log": false
        },
        "socketIoClientConfig": {
            "reconnectionAttempts": 50
        },
        "path": "/browser-sync/socket.io",
        "clientPath": "/browser-sync",
        "namespace": "/browser-sync",
        "clients": {
            "heartbeatTimeout": 5000
        }
    },
    "tagNames": {
        "less": "link",
        "scss": "link",
        "css": "link",
        "jpg": "img",
        "jpeg": "img",
        "png": "img",
        "svg": "img",
        "gif": "img",
        "js": "script"
    }
};

履歴

2017/07/24
初版発行。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その6 )( URL の決定 )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その5 )( Bootstrap, AdminLTE, Font Awesome, Ionicons のインストール ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • 画面の URL を決めます。

参照したサイト・書籍

目次

  1. 画面の URL を決める

手順

画面の URL を決める

各画面の URL は以下のようにします。

画面 アクション URL
入力画面1 初期表示 /inquiry/input/01/
「次へ」ボタンクリック時 /inquiry/input/01/?move=next
入力画面2 初期表示 /inquiry/input/02/
「前の画面へ戻る」ボタンクリック時 /inquiry/input/02/?move=back
「次へ」ボタンクリック時 /inquiry/input/02/?move=next
入力画面3 初期表示 /inquiry/input/03/
「前の画面へ戻る」ボタンクリック時 /inquiry/input/03/?move=back
「確認画面へ」ボタンクリック時 /inquiry/input/03/?move=next
確認画面 初期表示 /inquiry/confirm/
入力画面1の項目の「修正する」ボタンクリック時 /inquiry/input/01/
入力画面2の項目の「修正する」ボタンクリック時 /inquiry/input/02/
入力画面3の項目の「修正する」ボタンクリック時 /inquiry/input/03/
「送信する」ボタンクリック時 /inquiry/confirm/send/
完了画面 初期表示 /inquiry/complete/

履歴

2017/07/23
初版発行。
2017/08/06
* 入力画面3のボタン名を「次へ」→「確認画面へ」に変更しました。
2017/08/08
* HTML を作成した時に「前の画面へ戻る」ボタンに記述した CSS のスタイルを js-btn-back にしていたので URL も move=prevmove=back へ変更しました。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その5 )( Bootstrap, AdminLTE, Font Awesome, Ionicons のインストール )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その4 )( nodist + Node.js のインストール ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • npm を使用して Bootstrap, AdminLTE, Font Awesome, Ionicons をインストールします。
    • インストール後 src/main/resources/static へ必要なファイルをコピーする npm-scripts も作成します。

参照したサイト・書籍

  1. Purpose of installing bootstrap through npm?
    https://stackoverflow.com/questions/26773767/purpose-of-installing-bootstrap-through-npm

  2. npm でパッケージのバージョン一覧を確認したりバージョンを指定してインストールしたりする方法
    http://phiary.me/npm-package-version-info-install/

  3. npm scriptsを使おう
    http://qiita.com/liply/items/cccc6a7b703c1d3ab04f

  4. npm-scripts で使える便利モジュールたち
    http://qiita.com/mysticatea/items/12bb6579b9155fd74586

  5. cpx と rimraf を試す
    http://akabeko.me/blog/2015/09/cpx-rimraf/

  6. cpx
    https://www.npmjs.com/package/cpx

  7. rimraf
    https://www.npmjs.com/package/rimraf

  8. npm-run-all
    https://www.npmjs.com/package/npm-run-all

  9. npm runコマンドを実行したら警告が出るようになった
    http://qiita.com/isamusuzuki/items/738a2736a746b67bd977

目次

  1. package.json を生成する
  2. Bootstrap をインストールする
  3. AdminLTE をインストールする
  4. Font Awesome をインストールする
  5. Ionicons をインストールする
  6. install 後に node_modules → src/main/resources/static へファイルをコピーする npm-scripts を作成する
  7. AdminLTE の starter.html で確認する
  8. .gitignore を変更する
  9. 最後に

手順

package.json を生成する

  1. npm init -y コマンドを実行して package.json を生成します。

    f:id:ksby:20170722145004p:plain

    今は生成されたままとし、特に変更はしません。

Bootstrap をインストールする

  1. npm info bootstrap versions コマンドを実行してインストール可能な Bootstrap のバージョン一覧を表示します。

    f:id:ksby:20170722150304p:plain

  2. npm install --save bootstrap コマンドを実行して Bootstrap をインストールします。bootstrap@3.3.7 と表示されており、3.3.7 がインストールされました。

    f:id:ksby:20170722150624p:plain

  3. インストール先の node_modules/bootstrap の下を見ると以下のディレクトリ構成になっていました。単に使用したいのであれば dist ディレクトリの下のファイルを使えば良さそうです。

    f:id:ksby:20170722151144p:plain

AdminLTE をインストールする

  1. Google で “npm AdminLTE” で検索すると https://www.npmjs.com/package/admin-lte のページがヒットしました。npm でインストールする時のパッケージ名は admin-lte のようです。

    npm install --save admin-lte コマンドを実行して Bootstrap をインストールします。

    f:id:ksby:20170722152034p:plain

  2. インストール先の node_modules/admin-lte の下を見ると以下のディレクトリ構成になっていました。こちらも単に使用したいのであれば dist ディレクトリの下のファイルを使えば良さそうです。

    f:id:ksby:20170722152414p:plain

    jQuery も node_modules/admin-lte/plugins/jQuery の下に jquery-2.2.3.min.js があります。今回はこのファイルを使用します。

    f:id:ksby:20170722152615p:plain

Font Awesome をインストールする

  1. “npm Font Awesome” で検索すると https://www.npmjs.com/package/font-awesome がヒットしました。

    npm install --save font-awesome コマンドを実行して Font Awesome をインストールします。

    f:id:ksby:20170722174852p:plain

  2. インストール先の node_modules/font-awesome の下を見ると以下のディレクトリ構成になっていました。単に使用したいのであれば css, fonts ディレクトリの下のファイルを使えば良さそうです。

    f:id:ksby:20170722175203p:plain

Ionicons をインストールする

  1. “npm Ionicons” で検索すると https://www.npmjs.com/package/ionicons がヒットしました。

    npm install --save ionicons コマンドを実行して Ionicons をインストールします。

    f:id:ksby:20170722175714p:plain

  2. インストール先の node_modules/font-awesome の下を見ると以下のディレクトリ構成になっていました。単に使用したいのであれば css, fonts ディレクトリの下のファイルを使えば良さそうです(dist に scss 用のファイルとかも入っていて分かりづらいです)。

    f:id:ksby:20170722180247p:plain

install 後に node_modules → src/main/resources/static へファイルをコピーする npm-scripts を作成する

以下のコマンドを実行して npm-scripts を作成する上で便利なパッケージをインストールします。

  • npm install --save-dev cpx
  • npm install --save-dev rimraf
  • npm install --save-dev npm-run-all

package.json の scripts にコピー用の script を定義します。パッケージのファイルは src/main/resources/static/vendor/<パッケージ名>/ の下にコピーします。

  "scripts": {
    ..........
    "postinstall": "run-s clean:static-dir copy:all",
    "clean:static-dir": "rimraf src/main/resources/static/*",
    "copy:all": "run-p copy:bootstrap copy:admin-lte copy:font-awesome copy:ionicons",
    "copy:bootstrap": "cpx node_modules/bootstrap/dist/**/* src/main/resources/static/vendor/bootstrap",
    "copy:admin-lte": "cpx node_modules/admin-lte/dist/**/* src/main/resources/static/vendor/admin-lte",
    "copy:font-awesome": "cpx node_modules/font-awesome/{css,fonts}/**/* src/main/resources/static/vendor/font-awesome",
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons"
  },
  • postinstall を追加し、npm install コマンドが実行された後にファイルのコピーを行う処理を記述します。run-s コマンドで clean:static-dir → copy:all の順に script を実行します。
  • clean:static-dir は src/main/resources/static の下のディレクトリ、ファイルを削除します。static ディレクトリ自体は削除しません。
  • copy:all は node_modules の下から必要なファイルを src/main/resources/static の下にコピーします。以下の4つの script を run-p コマンドで並列実行します。
    • copy:bootstrap は node_modules/bootstrap パッケージの下のファイルをコピーします。
    • copy:admin-lte は node_modules/admin-lte パッケージの下のファイルをコピーします。
    • copy:font-awesome は node_modules/font-awesome パッケージの下のファイルをコピーします。
    • copy:ionicons は node_modules/ionicons パッケージの下のファイルをコピーします。

npm install コマンドを実行してみます。

f:id:ksby:20170723004623p:plain f:id:ksby:20170723004808p:plain

The node binary used for scripts is C:\Nodist\bin\node.exe but npm is using C:\Nodist\v-x64\6.11.1\node.exe itself. Use the–scripts-prepend-node-pathoption to include the path for the node binary npm was executed with. というメッセージが大量に出力されました(というより npm install コマンドでパッケージをインストールしている時から出ていて WARN だったので気にしていなかっただけでしたが)。さすがに量が多いので出力されないようにする方法があるか確認します。

Web で調べたところ npm runコマンドを実行したら警告が出るようになった に解決方法が記載されていました。npm config set scripts-prepend-node-path true コマンドを実行します。

再度 npm install コマンドを実行します。

f:id:ksby:20170723005544p:plain f:id:ksby:20170723005653p:plain

まだ npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules\chokidar\node_modules\fsevents): というメッセージが出ます。

Web で調べたところ fsevents は Mac でしか使用できないパッケージのためメッセージが出力されていますが、WARN なので気にしなければよいようです。

src/main/resources/static の下を見ると以下の画像のようにディレクトリが作成されていました。

f:id:ksby:20170723011220p:plain

AdminLTE の starter.html で確認する

node_modules/admin-lte の下に starter.html がありますので、それを src/main/resources/static の下にコピーした後、以下の点を変更します。

  • <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"><link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  • <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"><link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  • <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css"><link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  • <link rel="stylesheet" href="dist/css/AdminLTE.min.css"><link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  • <link rel="stylesheet" href="dist/css/skins/skin-blue.min.css"><link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">
  • 以下の2行はコメントアウトします。
    • <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
    • <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
  • body タグ内の <img src="dist/img/...<img src="/vendor/admin-lte/img/...

head タグ内は以下のようになります。

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>AdminLTE 2 | Starter</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <!-- Bootstrap 3.3.6 -->
  <link rel="stylesheet" href="/vendor/bootstrap/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="/vendor/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="/vendor/ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/AdminLTE.min.css">
  <!-- AdminLTE Skins. We have chosen the skin-blue for this starter
        page. However, you can choose any other skin. Make sure you
        apply the skin class to the body tag so the changes take effect.
  -->
  <link rel="stylesheet" href="/vendor/admin-lte/css/skins/skin-blue.min.css">

  <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
  <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
  <!--&lt;!&ndash;[if lt IE 9]>-->
  <!--<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>-->
  <!--<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>-->
  <!--<![endif]&ndash;&gt;-->
</head>

bootRun タスクを実行して Tomcat を起動した後、http://localhost:8080/starter.html にアクセスすると画面が表示されました。

f:id:ksby:20170723013450p:plain

Tomcat を停止します。

.gitignore を変更する

  1. commit する前に .gitignore を以下の方針で変更します。

    • node_modules は commit の対象にしません。
    • src/main/resources/static も commit の対象にしません。
    • npm 実行時にエラーが出ると出力される npm-debug.log も commit の対象にしません。

    .gitignore を リンク先の内容 に変更します。

    commit すると変更した .gitignore と、生成した package.json だけでした。npm install コマンドを実行すれば必要なファイルがインストール + コピーされます。

最後に

npm + npm-scripts が便利ですね。最初 Node.js + npm 関連を調べた時は最近の流行りは grunt, gulp ではなく webpack になったというような記事を見ていたので、npm でインストールした Bootstrap の css ファイル等を src/main/resources/static の下にコピーしたい場合にも webpack でやるのかな、と思って試行錯誤していたのですが(ファイルをコピーするための copy-webpack-plugin は見つけました)、単なるコピーは npm-scripts の方が楽でした。

現時点での感想として、

  • npm-scripts と webpack を必要に応じて使い分けるのが良さそう。
  • コピーするだけのような簡単な処理だったりコマンド実行するような場合には npm-scripts で。
  • 自分で js ファイルを実装して、実行用に1つにまとめるような場合には webpack で。
  • SPA で js ファイル以外に css や html のファイルも1つにまとめる場合も webpack で。
  • Gulpで始めるwebpack 3入門 という記事も見かけました。どうも大規模開発になったら webpack に gulp を組み合わせるといいらしいですが、今の自分ではその境地には至れず。。。 npm-scripts + webpack で進めて、必要性を感じたら gulp を使う流れかな? 今は無理に手を出さないことにします。

今回の作業で生成・変更した package.json の最終版を リンク先 に記載しておきます。

ソースコード

.gitignore

# built application files
*.apk
*.ap_

# files for the dex VM
*.dex

# Java class files
*.class

# generated files
**/bin/
**/gen/

# Local configuration file (sdk path, etc)
local.properties

# Eclipse project files
.classpath
.project

# Proguard folder generated by Eclipse
**/proguard/

# Intellij project files
*.iml
*.ipr
*.iws
**/.idea/
**/out/
**/src/main/resources/static

#Gradle
.gradletasknamecache
**/.gradle/
**/build/
**/bin/

#Node.js
**/node_modules/
npm-debug.log
  • **/src/main/resources/static を追加します。
  • #Node.js から下の行を追加します。

package.json

{
  "name": "boot-npm-geb-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "run-s clean:static-dir copy:all",
    "clean:static-dir": "rimraf src/main/resources/static/*",
    "copy:all": "run-p copy:bootstrap copy:admin-lte copy:font-awesome copy:ionicons",
    "copy:bootstrap": "cpx node_modules/bootstrap/dist/**/* src/main/resources/static/vendor/bootstrap",
    "copy:admin-lte": "cpx node_modules/admin-lte/dist/**/* src/main/resources/static/vendor/admin-lte",
    "copy:font-awesome": "cpx node_modules/font-awesome/{css,fonts}/**/* src/main/resources/static/vendor/font-awesome",
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "admin-lte": "^2.3.11",
    "bootstrap": "^3.3.7",
    "font-awesome": "^4.7.0",
    "ionicons": "^3.0.0"
  },
  "devDependencies": {
    "cpx": "^1.5.0",
    "npm-run-all": "^4.0.2",
    "rimraf": "^2.6.1"
  }
}

履歴

2017/07/23
初版発行。