今日の一枚 image


ダイアログは一度に一つしか出ないので個別にbooleanで管理するのではなく名前で管理するのが良いと思っている type TDialog = "" | "AddFusen"; ts

const AddFusenDialog = () => {
  const [dialog, setDialog] = useGlobal("dialog");
  const open = dialog === "AddFusen";
  const onClose = () => {
    setDialog("");
  };
 
  return (
    <Dialog open={open} fullWidth={true} fullScreen={true} onClose={onClose}>
...

ts

describe("dialog", () => {
  beforeEach(() => {
    cy.visit("/");
  });
 
  it("main", () => {
    cy.contains("Add Fusens").should("not.exist");
    cy.updateGlobal((g) => {
      g.dialog = "AddFusen";
    });
    cy.contains("Add Fusens").should("exist");
    cy.contains("Close").click();
    cy.contains("Add Fusens").should("not.exist");     
  });
});

image

ところでユーザに見せる名前をFusenにするかどうか

  • 前の実装ではPieceだった
  • Postitは他社の商標なので嫌
  • StickyNoteは長い、Stickyにするとネバネバしてそう
  • Stickerだと絵が描いてありそう
  • などもあって、日本語で押し通そうかと思ってる
  • 日本人に対しても既存の物理的な付箋から離れて新しい概念を表現する語として、いっそこざねにするのが良いのではという気もする
  • 白い背景「ホワイトボード」というとペンで書きそう、「キャンバス」はカラフルな絵を描きそう、という話
    • これもいっそ「場」でいいのでは
    • Kozane / Ba
      • いっそアプリの名前自体Kozanebaでいいのでは

メニューバーに貴重な画面スペースを独占させない強い意志…

  • image
    • もっと透明度を上げたい、背景色をもっと濃くすればいいのか?

メニュー

  • Document
  • https://material-ui.com/components/menus/
  • コンポーネントのローカルの状態に依存してるのだが、各メニューボタンのハンドラとかを別のモジュールに括り出した時に厄介
    • 前回は「処理が終わったら閉じる」ラッパーを作ったが…
    • そもそもメニューもダイアログ同様に一度に一つか?
      • 子メニューがあり得る
        • その場合に親の選択肢を出し続ける意味はないのだからやはり一度に一つで良いのでは?

付箋にもメニューがでる

  • しかし出る場所が微妙に気に入らない
  • image

気に入らないので直した

  • これがデフォルト挙動であるべきなのでは感
  • メニューを表示する位置を指定することができないので見えないdivをマウスの位置に動かしている
  • image

こざね法を読み返してみて、自分が作ってるものはKJ法よりはこざね法の方だなぁという気がした

KJ法…複数の人たちの「衆知をあつめる」法として、おおいに評価されて、各種の企業でも実用化されているようだ。わたしがここに紹介したこざね法というのは、単数個人用の、いわば密室むき知的生産技術であって、川喜田君の体系でいえば、比較的素朴で、初歩的な技法に属する。かれの体系のなかでは、「KJ法B型による文章化」とよばれているものと、ほぼおなじである。

drag/drop

  • image
  • ドラッグドロップができた
  • HTML5 Drag&Drop API
  • ただしドラッグ開始時に掴んだところがどこであるかと無関係にドロップ位置に中心を移動させてしまうので、開始時にズレを記録しておいて補正してやる必要がある
  • スクリーン座標系とワールド座標系との変換を一旦真面目にテストして作った方がいいかもなー
    • きっとこれに限らず色々なところで使うはずだし

座標系変換を実装したがテストが難しい。下記ではダメ test.ts

cy.testid("2").should(
  "hasPosition",
  movidea.world_to_screen([top, left])
);

なぜなら時刻T1で座標変換が先に処理されてしまうから。後から非同期でリトライをしても「目標とすべき値」は先に計算して固定されてるので意味がない。 test.ts

cy.testid("2").should(
  "hasPosition",
  T1
);

座標計算自体をリトライさせるには…と、考えてthenの中に入れた test.ts

cy.testid("2").then((el) => {
  return cy
    .wrap(el)
    .should("hasPosition", movidea.world_to_screen([top, left]));

これでも直前の状態更新が非同期だとうまくいかない、デフォルト非同期にしてたが、デフォルト同期にして非同期が必要な時だけオプションで指定することにした、なぜか同期でもテストは全部通った。

翌日の追記

  • これ、良く考えると画面の描画も座標変換も両方とも「現在の状態」に依存するものなのが原因なので、座標変換に必要な値を全部明示的に渡すピュアな関数があればよかったのでは

中央以外をつかんでドラッグするテスト、2回目のドラッグだけ不可解なズレが発生したり、元に戻るはずの中央ドラッグで全然元に戻らなかったりする

  • Cypressでドラッグのテストしてたら不可解な位置ずれズレが発生してどういうことかと気になってたんだけど、これおそらく「画面外に一部はみ出す位置」にドラッグで移動した後、次にドラッグする時にCypressのUIが対象を表示しようとしてスクロールしてしまうせいで座標がズレてるんだな…
  • つまりoverflow: hiddenでwheelイベントがpreventDefaultされてるアプリにおいてもCypressはドラッグ対象を表示しようとしてスクロールしてしまうのでclientXではなくpageXを使うべきだったということか
  • あれ、違うな…。その修正をしても「不可解な挙動」は再現する。
  • とりあえずはみ出さないテストケースでは期待通りに動くことがわかったので、はみ出した時の挙動は現状の挙動を記述したものとして分離しておこう。
  • この件、手ではみ出すパターンの操作をしたけど問題なく期待通りに動作したので僕の書いたアプリを人間が使う上では問題はなくCypressが使うと変な挙動になるってだけなのでスルーすることにした。

中央以外を掴んでドラッグができるようになった

  • image
  • 最初一回空振りするのはウィンドウにフォーカスが当たってなかったせい