ESETでWebブラウザの枠が緑色になるのを解除する

ESET Internet Securityのアップデートをしたら、Webブラウザのウィンドウ枠が緑色になった。

OSから何からすべてダークテーマにしていると、緑が目立って気持ち悪い。

無効化する方法を調べたのでメモ。

環境

Windows 10 Pro 21H2, ESET Internet Security 16.0.22.0。

問題

ESET Internet Securityのアップデートを行ったところ、Google ChromeFirefoxMicrosoft Edgeなど、Webブラウザを開いた時に、ウィンドウ枠が緑色になる。

Firefoxの場合

対応

一時的に解除

ブラウザ右に表示されている、ESETのアイコンをクリックすると、ポップアップが表示される。

そこの「ブラウザーの緑色のフレームを非表示」にチェックすると、そのウィンドウでは枠が消える。

恒久的に解除

一時的な解除方法では、ブラウザを閉じて開きなおすたびに毎回設定する必要がある模様。

恒久的に枠を消すには、ESET Internet Securityを開き、以下の設定を行う。

  1. 「設定」 > 「インターネット保護」をクリック
  2. 「Webアクセス保護」右の歯車アイコンをクリック
  3. 「インターネットバンキング保護」をクリックし、「ブラウザーの緑色のフレーム」のラジオボタンをクリックでOFFにし、「OK」ボタンをクリック

この設定を行っても、ブラウザを起動すると緑枠が表示されるが、数秒で消える。

振り返り

今回のアップデートで追加された機能なんだろうか。

別アプリケーションのUIを変えるというのは、お行儀がよくないなぁ...

設定変更個所も深くて面倒だし、設定しても一瞬表示されるしスッキリしない。

枠に色付けしたいモチベーションは何なんだろうか。

Storybookにアイコンでのロケール変更機能を追加する

React + Next.js で、多言語対応したアプリケーションを開発している。

日本語から、英語などのラテン文字で記述する言語に切り替えた場合、文字が長くなり、レイアウトが崩れることがあるため、各言語でのレイアウトを簡潔に確認したい。

アプリケーション側にロケール切り替え機能を用意しているが、デザイナーなど非エンジニアでも確認しやすいよう、Storybookにロケール切り替えアイコンを追加する方法を調べたのでメモ。

環境

名前 バージョン
React 18.2.0
Next.js 12.1.6
Storybook 6.5.13
Storybook Addon Next 1.6.10

Storybookへのアイコン追加方法

Storybookに、ロケール切り替えアイコンを追加したい。Storybook Addon Contextsstorybook-addon-localeなどの拡張機能も見つかったが、公式ガイドのToolbars & globalsに、アイコン追加方法の記載があった。

.storybook/preview.jsglobalTypes を宣言するだけで、アイコンを追加できる。 globalTypes は、ストーリー内では context.globals として参照可能。

また、Advanced usageに、そのものズバリなロケール切り替えのアイコン追加例が記載されている。

storybook.js.org

今回のユースケースであれば、 globalTypes で十分なため、この方法を使用。

toolbar.iconで指定可能な文字列

以下の What icons are available for my toolbar or my addon? に文字列の一覧、および実際にアイコンを表示するStorybookへのリンクがある。

storybook.js.org

記事作成時点でのStorybookへのリンクはこちら

ロケールのNext.jsアプリケーションへの反映

Next.jsアプリケーションでStorybookを扱う際の問題を一括で対応してくれるアドオン、Storybook Addon Nextを使用している。

storybook.js.org

Next.jsではロケールを参照するのに useRouter を用いるが、Storybook Addon Nextを用いるとRouterをモックしてくれる。

上記リンクの「Default Router」に記載があるが、モックされたRouterのlocaleは context?.globals?.locale を参照しているため、 globalTypeslocale を変更できるようにすればいい。

const defaultRouter = {
  locale: context?.globals?.locale,
  ...
}

ロケール切り替えアイコンの設定

.storybook/preview.js に、以下を追加。

export const globalTypes = {
  locale: {
    name: 'Locale',
    description: 'Change locale', // アイコンホバー時に表示
    defaultValue: 'ja-JP',
    toolbar: {
      icon: 'globe',
      items: [
        { value: 'ja-JP', right: 'ja-JP', title: '日本語' },
        { value: 'en-US', right: 'en-US', title: 'American English' },
        { value: 'en-GB', right: 'en-GB', title: 'British English' },
      ],
    },
  },
}

これで、地球儀アイコンが追加される。クリックすると、以下のようにメニューが展開され、いずれかの言語をクリックすることで context.globals.localetoolbar.itemsvalue で指定した値が設定される。

アイコンの表示位置について

globalTypes で追加したアイコンは、拡張機能のアイコンの左に、区切り線なしで追加される。表示位置の調整や、セパレーターの追加などが可能かは未確認。

(調査中に、位置を調整したいというissueが対応不要でcloseされているのを見かけた気がするが、確認できなかった)

stories.tsx 内での globals の参照方法

今回は使わなかったが、Story関数の第2引数に globals が設定されているため、Reactであれば以下の記述で各ストーリー内で globals を参照できる。

import { Story } from '@storybook/react'

import { MyComponent } from 'components/myComponent'

export default { component: MyComponent }

export const Default: Story = (_, { globals }) => (
  <MyComponent locale={globals.locale} />
)

React以外でストーリーを記述している場合、以下を参照。

storybook.js.org

振り返り

「Storybook ロケール切り替え」あたりで検索すると、 react-i18nextvue-i18n の例ばかり出てくるので、面倒なのかと思い先延ばしにしていた。

いよいよ待ったなしになったので、本腰入れて調査したら、アドオンなど使わなくても設定できたので一安心。

また、さらっと流しているが、Storybookのロケール設定がNext.jsに反映されるのも、Storybook Addon Nextのおかげ。 next/image 動かない問題なども解消してくれるので、Next.jsアプリケーションでStorybookを使う時は必須だと思う、ありがたや。

npm installで使える国旗アイコンセット

Node.js のアプリケーション内で、国旗の画像を使うことになった。

画像を用意するのが面倒だったので、 npm install して使える国旗のアイコンセットがないか調べたのでメモ。

候補

flag-icons

www.npmjs.com

2022年6月からダウンロード数が急増し、おそらく現在最もダウンロードされている国旗アイコンセット。ライセンスはMIT。著名なパッケージから依存されたのかな?

CSSを読み込み、クラス名として fi fi-<国> を指定する。

import '/node_modules/flag-icons/css/flag-icons.min.css'

<span class="fi fi-us"></span>

country-flag-icons

www.npmjs.com

ソースコードのホスト先がGitLabなので、Starは少ないが、安定的にダウンロード数が多い国旗アイコンセット。ライセンスはMIT。

READMEに記載があるが、過去にGitHubアカウントが停止されたことがある模様。Starが少ないのはそれも原因か。

Reactの場合、国ごとに import して使用する。

import { US } from 'country-flag-icons/react/3x2'

<US title="United States" className="..." />

flagpack

www.npmjs.com

country-flag-iconsのREADMEからリンクされていた。

FlagKitのnpmパッケージ版。ライセンスはMIT。

flag-icons と同じように、クラス名として fp および国を指定する。

<span class="fp us"></span>

Iconify

www.npmjs.com

Iconifyで利用可能なアイコンセットに、国旗アイコンが含まれている。

Iconify自体のライセンスはISC。利用するアイコンセットごとにライセンスが異なるが、例えば Maps / FlagsFlag IconsはMITライセンスだった。

他のアイコンと同様、アイコンを文字列で指定する。

import { Icon } from '@iconify/react'

<Icon icon="flag:us-4x3" />

パッケージ選定

クラス名で国を指定するタイプは、CSSライブラリとしてTailwindCSSを使っており、あまりクラス名を追加したくなかったため見送り。

残るcountry-flag-iconsとIconifyについてはどちらでもよかったが、すでにIconifyを使用していたため、そのまま使うことにした。

その他

svg-country-flags

www.npmjs.com

SVGおよびPNGのアイコンセット。ライセンスが明記されていなかったので見送り。

intl-tel-input

www.npmjs.com

電話番号の国番号(日本なら+81)の選択に使えるパッケージ。国旗と国番号を表示できる模様。

ユースケースに合わなかったので見送ったが、機会があれば使ってみたい。

振り返り

昔はアイコンセットをダウンロードして展開していたが、楽になったなぁ...

ダウンロード数の比較はこちら。flag-iconsの伸びは何が原因なんだろう?

npmtrends.com

ESLintでスクリプトファイル名のフォーマットを指定する

Next.js + TypeScriptのプロジェクトで、TypeScriptファイル名はローワーキャメルケースにするようコーディング規約を定めていた。

ただ、Javaのように言語側でフォーマットが決まっていないからか、個人の癖が出やすく、アッパーキャメルケース、スネークケース、ケバブケースと、異なる形式で書かれることがあった。

コードレビューでもファイル名については見落とされがちだったので、ファイル名のフォーマットを指定できないか調べたところ、ESLintでできたのでメモ。

ファイル名のフォーマットを指定するESLintプラグイン

ざっと調べただけでいくつかでてきたが、最終更新日が古く、TypeScript未対応だったりする。

今回は、XOにも使われているeslint-plugin-unicornに、ファイル名に関するルールがあり、かつTypeScript対応していたため、こちらを使用する。

ルールの詳細は以下。

github.com

eslint-plugin-unicornのインストールとfilename-caseルールの有効化

yarn add -D eslint-plugin-unicorn でインストールし、.eslintrc のpluginsに unicorn を追加。

ルールを有効化するには、unicorn/filename-case をrulesに追加する。

また、推奨設定に含まれているため、extendsに plugin:unicorn/recommended を追加しても有効となる。なお、推奨設定を有効化すると、v44.0.2の時点で、96ルールが有効になるため注意。

以下、推奨設定の有効化の例。

{
  "extends": [
    "plugin:unicorn/recommended"
  ],
  "plugins": [
    "unicorn"
  ]
}

ファイル名のフォーマットを変更

これでファイル名に関するルールが有効となるが、デフォルトではケバブケース(foo-bar.拡張子)がフォーマットとして設定されている。

今回はローワーキャメルケース(booBar.拡張子)としたいので、rulesを以下の様に変更。

{
  "rules": {
    "unicorn/filename-case": ["error", { "case": "camelCase" }]
  }
}

casecamelCase を指定すると、ローワーキャメルケースとなる。

この他、 snakeCase でスネークケース、 pascalCase でアッパーキャメルケース。

Lint実行結果

yarn lint すると、ファイル拡張子が js,jsx,ts,tsx で、ファイル名のフォーマットが異なるファイルについては、そのファイルの1行目に以下の様なLintエラーが発生する。

Filename is not in <指定フォーマット名>. Rename it to <推奨ファイル名>.

クイックフィックスも可能。

驚いたのは、Next.jsで使用する pages/_app.tsxpages/_document.tsx といったファイルはエラーにならなかった。Next.jsを使っているか判断しているのか?

なお、明示的に除外したいファイル名がある場合、rulesの ignore で調整可能。

振り返り

ファイル名の形式違いをLintエラーとし、ビルドを失敗させることで、見落とすことなく修正できるようになった。

また、eslint-plugin-unicornで追加されたその他のルールも有用だったので、ソースコード品質の向上につながった。そっちもいろいろ面白いので、そのうちまとめたい。

daisyUIのアニメーションを無効化する

小ネタ。

daisyUIのアニメーションの無効化ができないか調べたのでメモ。

問題

daisyUIのコンポーネントを使うと、インタラクションの一環としてか、デフォルトでアニメーションが行われる。

ちょっと困ったのが、チェックボックスをReactコンポーネント化して使う際、プロパティとして checked を渡してあらかじめチェック済みにしても、チェックのアニメーションが流れてしまう。

下記デモページで PreviewHTMLJSX のタブを切り替えると確認できる。

daisyui.com

対応

アニメーションを抑制できないかと調べてみると、以下のissueを発見。

github.com

クラス名として no-animation を付与すると、個別にアニメーションを抑制できる。

ボタンのサンプルとしてButton without click animationにも使用されていた。

チェックボックスに付与すると、アニメーションせずにチェックされた状態で表示されるようになった。

clsx でクラス名を組み立てているので、 className={clsx(..., checked && 'no-animation')}className={clsx(..., { 'no-animation': checked })} のように指定することで、チェック済みならアニメーション無効化、といった制御ができた。

アニメーションを一括で無効化

tailwind.config.js にて、--animation-btn--animation-inputテーマから指定することで、アニメーションの持続時間を変更できる。

issueに記載があったが、この持続時間を0にすることで、アニメーションを一括で無効化できる模様。

/** @type {import('tailwindcss').Config} */
module.exports = {
  ...
  plugins: [require('daisyui')],
  daisyui: {
    themes: [
      {
        mytheme: {
          ...
          '--animation-input': 0,
        },
      },
    ],
  },
}

振り返り

こういった設定がサクッとできる、いい時代になったなぁ...

拡張子がtsxのファイル内で、型パラメータを持ったアロー関数を宣言する

小ネタ。

TypeScriptでは、アロー関数でジェネリック関数を定義できる。

const generic = <T>(value: T) => ...

拡張子が .ts のファイル内では問題ないが、 .tsx のファイル内でこの記法を用いると、 <T> がタグの開始とみなされ、以下のコンパイルエラーが発生する。

JSX 要素 'T' には対応する終了タグがありません。ts(17008)

別ファイルに分けるしかないかなと思って調べたところ、以下の記事を発見。

www.ashbyhq.com

記事の通り、型引数の後ろにカンマを付けると、エラーが解消された。

const generic = <T,>(value: T) => ...

JavaScriptで文字列を繰り返したり、配列を特定の値で埋める

JestやStorybookで、テストや表示用に長い文字列や配列を用意するがある。

リテラルで書いていた同僚に、コードレビューで「こんなやり方ができるよ」と教えたら感心されたのでメモ。

文字列

文字列の繰り返し

String.repeat(繰り返し回数) で繰り返し可能。

developer.mozilla.org

console.log('a'.repeat(5)) // aaaaa
console.log('abc'.repeat(2)) // abcabc

左埋め・右埋め

左埋めは String.padStart(桁数, 埋める文字列) で、右埋めは String.padStart(桁数, 埋める文字列) で文字埋め可能。埋める文字列を省略すると、半角スペースで埋められる。

developer.mozilla.org

developer.mozilla.org

console.log('123'.padStart(5, '0')) // 00123
console.log('123'.padEnd(5, '0')) // 12300

配列

配列を特定の値で埋める

Array.fill(埋める値) で、配列の値を埋めることができる。

developer.mozilla.org

任意の長さの配列を作るには、長さを指定したコンストラクタを使えばいい。

console.log(new Array(2).fill(1)) // [1, 1] 

// new は省略可能
console.log(Array(2).fill('a')) // [a, a] 
eslint-plugin-unicornのno-new-arrayルール対応

個人的にお気に入りの eslint-plugin-unicorn を使っていると、no-new-arrayルールでArrayコンストラクタ実行が怒られるため、 Array.from({length: 長さ}) を使用。

console.log(Array.from({ length: 2 }).fill(1)) // [1, 1]

ただ、この書き方だと、TypeScriptでは Array.from および fill の戻り値が uknown[] になる。

型指定が必要な場合、 Array.from<型>(...) のように記述すればいい。

const strings: string[] = Array.from<string>({ length: 2 }).fill('a')

配列の繰り返し

配列を繰り返したい場合、 Array.fill(繰り返したい配列).flat() で可能。

console.log(Array(2).fill([1, 2]).flat()) // [1, 2, 1, 2]

TypeScriptで Array.from に型指定する場合、繰り返したい配列の型を指定する必要がある。

const numbers: number[] = Array.from<number[]>({ length: 2 })
  .fill([1, 2, 3])
  .flat()

console.log(numbers) // [1, 2, 3, 1, 2, 3]

振り返り

Storybookであればまだいいが、ユニットテストで長い文字列が使われていると、念のため文字数を数えたりと、本来注視しなくていい部分を確認する必要が生じる。

文字数制限のテストなどで、n文字以上であればエラーとなることをテストしたい場合、例えば n = 21 であれば 'a'.repeat(21) のように書かれているほうが個人的には読みやすい。

レビュアー目線からはこんな書き方のほうがありがたいと思い共有したが、同僚からも楽に書けるということで概ね好評だった。

文字列の左右埋めや、 Array.flat() はちょこちょこプロダクトコードでも使うので、覚えておいて損はないと思う。