Files
hipmi-mobile/docs/NEWWRAPPER-KEYBOARD-IMPLEMENTATION.md
bagasbanuna 90bc8ae343 feat: Migrate Job screens to NewWrapper_V2 with keyboard handling
- Migrate ScreenJobCreate.tsx to NewWrapper_V2
    - Migrate ScreenJobEdit.tsx to NewWrapper_V2
    - Add NewWrapper_V2 component with auto-scroll keyboard handling
    - Add useKeyboardForm hook for keyboard management
    - Add FormWrapper component for forms
    - Create ScreenJobEdit.tsx from edit route (separation of concerns)
    - Add documentation for keyboard implementation
    - Add TASK-004 migration plan
    - Fix: Footer width 100% with safe positioning
    - Fix: Content padding bottom 80px for navigation bar
    - Fix: Auto-scroll to focused input
    - Fix: No white area when keyboard close
    - Fix: Footer not raised after keyboard close

    Phase 1 completed: Job screens migrated

### No Issue
2026-04-02 15:07:10 +08:00

8.5 KiB

NewWrapper Keyboard Handling Implementation

📋 Problem Statement

NewWrapper saat ini memiliki masalah keyboard handling pada Android:

  • Footer terangkat saat keyboard close
  • Muncul area putih di bawah
  • Input terpotong saat keyboard muncul
  • Tidak ada auto-scroll ke focused input

🔍 Root Cause Analysis

Current NewWrapper Structure

<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : "height"}>
  <View style={{ flex: 0 }}>  // ← MASALAH 1: flex: 0
    <ScrollView>
      {children}
    </ScrollView>
  </View>
  <View style={{ position: "absolute" }}>  // ← MASALAH 2: position absolute
    {footerComponent}
  </View>
</KeyboardAvoidingView>

Issues Identified

Issue Impact Severity
behavior="height" di Android View di-resize, content terpotong 🔴 High
flex: 0 pada View wrapper ScrollView tidak expand dengan benar 🔴 High
Footer dengan position: absolute Footer tidak ikut layout flow 🟡 Medium
Tidak ada keyboard event handling Tidak ada auto-scroll ke input 🟡 Medium

💡 Proposed Solutions

Option A: Full Integration (Breaking Changes)

Replace entire KeyboardAvoidingView logic dengan keyboard handling baru.

// NewWrapper.tsx
export function NewWrapper({ children, footerComponent }: Props) {
  const { scrollViewRef, createFocusHandler } = useKeyboardForm();
  
  return (
    <KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : undefined}>
      <ScrollView ref={scrollViewRef} style={{ flex: 1 }}>
        {children}
      </ScrollView>
      <SafeAreaView style={{ position: 'absolute', bottom: 0 }}>
        {footerComponent}
      </SafeAreaView>
    </KeyboardAvoidingView>
  );
}

Pros:

  • Clean implementation
  • Consistent behavior across all screens
  • Single source of truth

Cons:

  • Breaking changes - Semua screen yang pakai NewWrapper akan affected
  • Need to add onFocus handlers to all TextInput/TextArea components
  • High risk - May break existing screens
  • Requires testing all screens that use NewWrapper

Impact:

  • All existing screens using NewWrapper will be affected
  • Need to add onFocus handlers to all inputs
  • Need to wrap inputs with View onStartShouldSetResponder

Add flag to enable keyboard handling optionally (backward compatible).

// NewWrapper.tsx
interface NewWrapperProps {
  // ... existing props
  enableKeyboardHandling?: boolean;  // Default: false
  keyboardScrollOffset?: number;     // Default: 100
}

export function NewWrapper(props: NewWrapperProps) {
  const { 
    enableKeyboardHandling = false,
    keyboardScrollOffset = 100,
    ...rest 
  } = props;

  // Use keyboard hook if enabled
  const keyboardForm = enableKeyboardHandling 
    ? useKeyboardForm(keyboardScrollOffset) 
    : null;

  // Render different structure based on flag
  if (enableKeyboardHandling && keyboardForm) {
    return renderWithKeyboardHandling(rest, keyboardForm);
  }
  
  return renderOriginal(rest);
}

Pros:

  • Backward compatible - No breaking changes
  • Opt-in - Screens yang butuh bisa enable
  • Safe - Existing screens tetap bekerja
  • Gradual migration - Bisa migrate screen by screen
  • Low risk - Can test with new screens first

Cons:

  • ⚠️ More code (duplicate logic)
  • ⚠️ Need to maintain 2 implementations temporarily

Usage Example:

// Existing screens - No changes needed!
<NewWrapper footerComponent={<Footer />}>
  <Content />
</NewWrapper>

// New screens with forms - Enable keyboard handling
<NewWrapper 
  enableKeyboardHandling
  keyboardScrollOffset={100}
  footerComponent={<Footer />}
>
  <View onStartShouldSetResponder={() => true}>
    <TextInputCustom onFocus={keyboardForm.createFocusHandler()} />
  </View>
</NewWrapper>

Option C: Create New Component (Safest)

Keep NewWrapper as is, create separate component for forms.

// Keep NewWrapper unchanged
// Use FormWrapper for forms (already created!)

Pros:

  • Zero risk - NewWrapper tidak berubah
  • Clear separation - Old vs New
  • Safe for existing screens
  • FormWrapper already exists!

Cons:

  • ⚠️ Multiple wrapper components
  • ⚠️ Confusion which one to use

Usage:

// For regular screens
<NewWrapper>{content}</NewWrapper>

// For form screens
<FormWrapper footerComponent={<Footer />}>
  <TextInputCustom />
</FormWrapper>

📊 Comparison Matrix

Criteria Option A Option B Option C
Backward Compatible
Implementation Effort High Medium Low
Risk Level 🔴 High 🟡 Medium 🟢 Low
Code Duplication None Temporary Permanent
Migration Required Yes Gradual No
Testing Required All screens New screens only New screens only
Recommended For Greenfield projects Existing projects Conservative teams

Implementation Plan

Phase 1: Add Keyboard Handling to NewWrapper (Week 1)

// Add to NewWrapper interface
interface NewWrapperProps {
  enableKeyboardHandling?: boolean;
  keyboardScrollOffset?: number;
}

// Implement dual rendering logic
if (enableKeyboardHandling) {
  return renderWithKeyboardHandling(props);
}
return renderOriginal(props);

Phase 2: Test with New Screens (Week 2)

  • Test with Job Create 2 screen
  • Verify auto-scroll works
  • Verify footer stays in place
  • Test on iOS and Android

Phase 3: Gradual Migration (Week 3-4)

Migrate screens one by one:

  1. Event Create
  2. Donation Create
  3. Investment Create
  4. Voting Create
  5. Profile Create/Edit

Phase 4: Make Default (Next Major Version)

After thorough testing:

  • Make enableKeyboardHandling default to true
  • Deprecate old behavior
  • Remove old code in next major version

📝 Technical Requirements

For NewWrapper with Keyboard Handling

// 1. Import hook
import { useKeyboardForm } from "@/hooks/useKeyboardForm";

// 2. Use hook in component
const { scrollViewRef, createFocusHandler } = useKeyboardForm(100);

// 3. Pass ref to ScrollView
<ScrollView ref={scrollViewRef}>

// 4. Wrap inputs with View
<View onStartShouldSetResponder={() => true}>
  <TextInputCustom onFocus={createFocusHandler()} />
</View>

Required Changes per Screen

For each screen that enables keyboard handling:

  1. Add enableKeyboardHandling prop
  2. Wrap all TextInput/TextArea with View
  3. Add onFocus handler to inputs
  4. Test thoroughly

🧪 Testing Checklist

For Each Screen

  • Tap Input 1 → Auto-scroll to input
  • Tap Input 2 → Auto-scroll to input
  • Tap Input 3 → Auto-scroll to input
  • Dismiss keyboard → Footer returns to position
  • No white area at bottom
  • Footer not raised
  • Smooth transitions
  • iOS compatibility
  • Android compatibility

Platforms to Test

  • Android with navigation buttons
  • Android with gesture navigation
  • iOS with home button
  • iOS with gesture (notch)
  • Various screen sizes

📋 Decision Factors

Choose Option A if:

  • Project is new (few existing screens)
  • Team has time for full migration
  • Want clean codebase immediately
  • Accept short-term disruption

Choose Option B if:

  • Existing project with many screens
  • Want zero disruption to users
  • Prefer gradual migration
  • Want to test thoroughly first

Choose Option C if:

  • Very conservative team
  • Cannot risk any changes to existing screens
  • OK with multiple wrapper components
  • FormWrapper is sufficient

🚀 Next Steps

  1. Review this document with team
  2. Decide on approach (A, B, or C)
  3. Create implementation ticket
  4. Start with Phase 1
  5. Test thoroughly
  6. Roll out gradually

  • components/_ShareComponent/NewWrapper.tsx - Current wrapper
  • components/_ShareComponent/FormWrapper.tsx - New form wrapper
  • hooks/useKeyboardForm.ts - Keyboard handling hook
  • screens/Job/ScreenJobCreate2.tsx - Example implementation

📞 Discussion Points

  1. Which option do you prefer? (A, B, or C)
  2. How many screens use NewWrapper?
  3. Team capacity for migration?
  4. Timeline for implementation?
  5. Risk tolerance level?

Last Updated: 2026-04-01 Status: 📝 Under Discussion