smarthr-ui icon indicating copy to clipboard operation
smarthr-ui copied to clipboard

feat: Stepを持つダイアログをキーボードフォーカスしやすく開発しやすくするカスタムフックを作る

Open masuP9 opened this issue 1 year ago • 3 comments

Related URL

Overview

useDialogStepsを作成

返り値

type UseDialogStepsResult = [
  number, // currentStep
  {
    setStep: React.Dispatch<React.SetStateAction<number>> // stepをset
    nextStep: () => void // 次のstepへ
    prevStep: () => void // 前のstepへ
  },
  () => React.JSX.Element, // ステップ移動したときにフォーカスを当て直すための要素。通常は Dialog の children の最初に配置する
]

アプリケーション側の実装

// useDialogSteps()を実行して必要なものを作る
const [currentStep, { setStep, nextStep, prevStep }, renderFocusTarget] = useDialogSteps()

const dialogContents: DialogContents = {
  0: (各ステップごとのDialogのPropsを定義しておく),
  1: ...,
  2: ...,
}

const dialogContent = dialogContents[currentStep]

return (
  <ActionDialog
    {...dialogContent}
    title={`Stepper Dialog ${currentStep} / 2`}
    isOpen={currentStep > 0}
    onClickClose={currentStep === 2 ? () => setStep(0) : prevStep}
  >
    /* childrenの最初で、renderFocusTarget() を実行する。stepが変わったらこの位置に focus がリセットされる */
    {renderFocusTarget()}
    {dialogContent.children}
  </ActionDialog>
)

What I did

Capture

masuP9 avatar Jul 04 '24 02:07 masuP9

検討している実装方法

  • focusTargetRef のような、Dialog に渡すための値を返り値に持つ useDialogSteps を提供する
    • メリット
      • 実装側が Dialog, ActionDialog 関係なく使える
    • デメリット
    • ref を Dialog に渡し忘れがありそう
  • <StepsDialog> コンポーネントを実装する
    • メリット
      • 実装側で Dialog を持つため渡し忘れがない
    • デメリット
      • 開発チーム側で独自にダイアログを実装していた場合に対応できない

schktjm avatar Sep 02 '24 09:09 schktjm

focusTargetRef のような、Dialog に渡すための値を返り値に持つ useDialogSteps を提供する

返り値

type UseDialogStepsResult = [
  number, // currentStep
  {
    setStep: React.Dispatch<React.SetStateAction<number>> // stepをset
    nextStep: () => void // 次のstepへ
    prevStep: () => void // 前のstepへ
  },
  refObject<HTMLElement> // ステップ移動したときにフォーカスを当て直すための要素にわたすための ref。Dialog の props として渡す想定
]

アプリケーション側

// useDialogSteps()を実行して必要なものを作る
const [currentStep, { setStep, nextStep, prevStep }, focusTargetRef] = useDialogSteps()

const dialogContents: DialogContents = {
  0: (各ステップごとのDialogのPropsを定義しておく),
  1: ...,
  2: ...,
}

const dialogContent = dialogContents[currentStep]

return (
  <ActionDialog
    {...dialogContent}
    focusTargetRef={focusTargetRef}
    title={`Stepper Dialog ${currentStep} / 2`}
    isOpen={currentStep > 0}
    onClickClose={currentStep === 2 ? () => setStep(0) : prevStep}
  >
    {dialogContent.children}
  </ActionDialog>
)

schktjm avatar Sep 02 '24 09:09 schktjm

WIP: <StepsDialog> コンポーネントを実装する

イメージ

type Props = {
  onNext: (index: number) => void;
  onPrev: (index: number) => void;
  children: ReactNode;
  defaultIndex: number
} & ActionDialogProps

使う側

const [currentStep, setCurrentStep] = useState(0)

const dialogContents: DialogContents = {
  0: (各ステップごとのDialogのPropsを定義しておく),
  1: ...,
  2: ...,
}

const dialogContent = dialogContents[currentStep]

const onNextStep = (step: number) => {
  setCurrentStep(step)
}

return (
  <StepsDialog
    {...dialogContent}
    title={`Stepper Dialog ${currentStep} / 2`}
    isOpen={currentStep > 0}
    onClickClose={currentStep === 2 ? () => setStep(0) : prevStep}
    onNext={onNextStep}
  >
    {dialogContent.children}
  </StepsDialog>
)



schktjm avatar Sep 02 '24 10:09 schktjm

https://github.com/kufu/smarthr-ui/pull/5004 で解決予定だと思うのでcloseしちゃいます!

Qs-F avatar Jan 20 '25 09:01 Qs-F