onLongPress property not working in Button component

1. Summary

onLongPress property not working in Button component

App Name:
FrndlyTv App

Bug Severity
Select one that applies

  • Impacts operation of app

3. Observed Behaviour

In the login screen, when performing a long press on the Clear button, the onLongPress callback does not trigger.
Instead, the onPress event fires immediately, resulting in only a single character being cleared.

Because onLongPress never fires, the intended functionality—clearing the entire text in the TextInput—is not working.

4. Expected Behavior

If the user presses and holds the button for more than 250 ms, the onLongPress event should trigger, allowing the app to clear all the entered text as expected.

6. Environment

Please fill out the fields related to your bug below:

  • SDK Version: o0.20.3351

7. Example Code Snippet / Screenshots / Screengrabs

Include any relevant code or component setup in React Native that can help reproduce the bug.

  import { Button } from '@amzn/kepler-ui-components';
 {othrbtns.map((item, index) => (
            <Button
              key={index}
               label={
                item === 'x' || item === 'space' ? '' : item
              }
              iconSource={
                item === 'x'
                  ? backspaceIcon
                  : item === 'space'
                  ? spaceIcon // <-- Use your custom space icon here
                  : undefined
              }
              iconSize={'sm'}
              iconPosition="start"
              style={[
                styles.key,
                styles.twocol_btn,
                item === 'x' && { width: 235 },
                (item === '!#$' || item === 'abc') && { width: 176 },
                 item === 'space' && { width: 400 }
              ]}
              focusedStyle={[{
                width: 126,
                height: 72,
                justifyContent: 'center',
                alignItems: 'center',
                backgroundColor: '#00AD50',
                margin: 4,
                borderWidth: 0,
                borderRadius: 0,
              },
              item === 'x' && { width: 235 },
              (item === '!#$' || item === 'abc') && { width: 176 },
               item === 'space' && { width: 400 }
              ]}
              onPress={() =>
              item === 'x'
                ? handleBackspace()
                : item === '!#$' || item === 'abc'
                ? handleSpecialChar()
                : item === 'space'
                ? onKeyPress(' ')
                : onKeyPress(item)
            }
            onLongPress={()=>{console.log("triggering long Press");
            }}
              variant={'primary'}
            />
          ))}
const handleBackspace = () => {

    if (emailArrowVisible) {

setEmailTextInputValue((prevValue: string) => prevValue.slice(0, -1));

    } else {

setpassword(prevValue => prevValue.slice(0, -1));

    }

setPswErrorBorderVisible(false);

setemailErrorBorderVisible(false);

  };

Hey @vittalmaradi

Thanks for letting us know.
I’ll try to reproduce this issue and update you with my findings soon.

Warm regards,
Ivy

@vittalmaradi I used the code snippet you shared but was not able to reproduce your issue.
Let me share my App.tsx and LongPressTest.tsx code snippets.

App.tsx

import React, {useState} from 'react';
import {SafeAreaView, StyleSheet, TextInput, View, Text, TouchableOpacity} from 'react-native';

const App = () => {
  const [text, setText] = useState('');
  const [focusedKey, setFocusedKey] = useState('');

  const keys = [
    ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
    ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],
    ['Z', 'X', 'C', 'V', 'B', 'N', 'M'],
    ['SPACE', 'CLEAR']
  ];

  const [clearTimer, setClearTimer] = useState(null);

  const handleKeyPress = (key) => {
    console.log('Key pressed:', key);
    if (key === 'CLEAR') {
      setText(prev => prev.slice(0, -1));
    } else if (key === 'SPACE') {
      setText(text + ' ');
    } else {
      setText(text + key);
    }
  };

  const handleClearLongPress = () => {
    console.log('Clear long press started');
    const timer = setInterval(() => {
      setText(prev => prev.slice(0, -1));
    }, 100);
    setClearTimer(timer);
  };

  const handleClearPressOut = () => {
    if (clearTimer) {
      clearInterval(clearTimer);
      setClearTimer(null);
    }
  };

  return (
    <SafeAreaView style={styles.container}>
      <TextInput
        style={styles.textInput}
        value={text}
        onChangeText={setText}
        placeholder="Type here..."
        showSoftInputOnFocus={false}
      />
      <View style={styles.keyboard}>
        {keys.map((row, rowIndex) => (
          <View key={rowIndex} style={styles.row}>
            {row.map((key) => (
              <TouchableOpacity
                key={key}
                style={[styles.key, key === 'SPACE' && styles.spaceKey]}
                onPress={() => handleKeyPress(key)}
                onLongPress={key === 'CLEAR' ? handleClearLongPress : undefined}
                onPressOut={key === 'CLEAR' ? handleClearPressOut : undefined}
                onFocus={() => setFocusedKey(key)}
                onBlur={() => setFocusedKey('')}
              >
                <Text style={styles.keyText}>{key === 'SPACE' ? '___' : key}</Text>
              </TouchableOpacity>
            ))}
          </View>
        ))}
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#000',
    padding: 20,
  },
  textInput: {
    backgroundColor: '#fff',
    padding: 15,
    fontSize: 18,
    borderRadius: 5,
    marginBottom: 20,
  },
  keyboard: {
    flex: 1,
    justifyContent: 'center',
  },
  row: {
    flexDirection: 'row',
    justifyContent: 'center',
    marginBottom: 10,
  },
  key: {
    backgroundColor: '#333',
    padding: 15,
    margin: 5,
    borderRadius: 5,
    minWidth: 50,
    alignItems: 'center',
  },
  spaceKey: {
    minWidth: 200,
  },
  keyText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

export {App};
export default App;

LongPressTest.tsx

import React, { useState } from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';

const LongPressTest = () => {
  const [inputValue, setInputValue] = useState('');
  const [longPressCount, setLongPressCount] = useState(0);
  const [lastEvent, setLastEvent] = useState('');

  const othrbtns = ['a', 'b', 'c', 'x', 'space', '!#$', 'abc'];

  const onKeyPress = (key: string) => {
    setInputValue(prev => prev + key);
    setLastEvent(`Key pressed: ${key} at ${new Date().toLocaleTimeString()}`);
    console.log('🔘 onPress triggered:', key);
  };

  const handleBackspace = () => {
    setInputValue(prevValue => prevValue.slice(0, -1));
    setLastEvent(`Backspace at ${new Date().toLocaleTimeString()}`);
    console.log('🔙 Backspace triggered');
  };

  const handleSpecialChar = () => {
    console.log('🔣 Special char mode');
    setLastEvent(`Special char mode at ${new Date().toLocaleTimeString()}`);
  };

  const handleLongPress = () => {
    setLongPressCount(prev => prev + 1);
    setLastEvent(`LongPress #${longPressCount + 1} at ${new Date().toLocaleTimeString()}`);
    console.log('🔥 triggering long Press');
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>OnLongPress Kepler Button Test</Text>
      
      <Text style={styles.input}>{inputValue || 'Type here...'}</Text>
      
      <View style={styles.buttonGrid}>
        {othrbtns.map((item, index) => (
          <TouchableOpacity
            key={index}
            style={[
              styles.key,
              item === 'x' && { width: 235 },
              (item === '!#$' || item === 'abc') && { width: 176 },
              item === 'space' && { width: 400 }
            ]}
            onPress={() =>
              item === 'x'
                ? handleBackspace()
                : item === '!#$' || item === 'abc'
                ? handleSpecialChar()
                : item === 'space'
                ? onKeyPress(' ')
                : onKeyPress(item)
            }
            onLongPress={handleLongPress}
          >
            <Text style={styles.keyText}>
              {item === 'x' ? '⌫' : item === 'space' ? 'SPACE' : item}
            </Text>
          </TouchableOpacity>
        ))}
      </View>

      <View style={styles.stats}>
        <Text style={styles.statText}>Input: {inputValue}</Text>
        <Text style={styles.statText}>Long Press Count: {longPressCount}</Text>
        <Text style={styles.eventText}>{lastEvent}</Text>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  title: {
    color: '#fff',
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
    textAlign: 'center',
  },
  input: {
    color: '#fff',
    fontSize: 18,
    backgroundColor: '#333',
    padding: 15,
    borderRadius: 5,
    marginBottom: 20,
    minWidth: 300,
    textAlign: 'center',
  },
  buttonGrid: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'center',
    marginBottom: 20,
  },
  key: {
    width: 126,
    height: 72,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#007AFF',
    margin: 4,
    borderRadius: 5,
  },
  keyText: {
    color: '#fff',
    fontSize: 18,
    fontWeight: 'bold',
  },
  stats: {
    alignItems: 'center',
  },
  statText: {
    color: '#fff',
    fontSize: 16,
    marginBottom: 10,
  },
  eventText: {
    color: '#00ff00',
    fontSize: 14,
    textAlign: 'center',
  },
});

export default LongPressTest;

Please try this way and let me know if you still observe this issue.
FYI: I have kept ‘onLongpress’ event activation time to 100ms in the shared code snippet. However I have tried with 250ms activation time as well and it works.

Warm regards,
Ivy


Screen recording of the same.

Hi Team, thanks for the response.

In our keyboard component, we are using the Button component from @amzn/kepler-ui-components. We need onLongPress support directly in the Kepler Button. When we replace only the backspace key with TouchableOpacity to emulate long-press behavior, it introduces focus issues with the rest of the buttons on the screen.

Could you please provide the updated code or guidance for enabling onLongPress within the Kepler Button component itself, so that long-press can be handled without having to switch to TouchableOpacity?

Thanks.

We are checking @vittalmaradi , I’ll update you ASAP.

Hi @Ivy_Mahajan , Any update on this?

Hi @vittalmaradi

Apologies for the delayed response.

We have tried with three SDKs (0.20.3351 (reported), 0.20.3487 (latest 0.20), and 0.21.5245 (latest 0.21)).
SDKs 0.20 use the same repository, so the consumed library is the same (i.e. @amzn/kepler-ui-components 2.4.1).

In both cases the Button component works as advertised and emits onPress and onLongPress correctly (for a minimal Hello World-based app).

Here is our testing project code snippets:
App.tsx

import React, {useState} from 'react';
import {StyleSheet, Text, ImageBackground, View, Image} from 'react-native';
import {Link} from './components/Link';
import {Button} from '@amzn/kepler-ui-components';

const images = {
  kepler: require('./assets/kepler.png'),
  learn: require('./assets/learn.png'),
  support: require('./assets/support.png'),
  build: require('./assets/build.png'),
};

export const App = () => {
  const [image, setImage] = useState(images.kepler);

  const styles = getStyles();

  return (
    <ImageBackground
      source={require('./assets/background.png')}
      style={styles.background}>
      <View style={styles.container}>
        <View style={styles.links}>
          <View style={styles.headerContainer}>
            <Text style={styles.headerText}>Hello World!</Text>
            <Text style={styles.subHeaderText}>
              Select one of the options below to start your Kepler journey 🚀
            </Text>
          </View>
          <Button
            onPress={() => console.log('PRESS')}
            onLongPress={() => console.log('LONG PRESS')}
            variant={'primary'}
            label="Button title">
            Press me!
          </Button>
          <Link
            linkText={'Learn'}
            onPress={() => {
              setImage(images.learn);
            }}
            testID="sampleLink"
          />
          <Link
            linkText={'Build'}
            onPress={() => {
              setImage(images.build);
            }}
          />
          <Link
            linkText={'Support'}
            onPress={() => {
              setImage(images.support);
            }}
          />
        </View>
        <View style={styles.image}>
          <Image source={image} />
        </View>
      </View>
      <View style={styles.textContainer}>
        <Text style={styles.text}>
          💡 Edit App.tsx to change this screen and then come back to see your
          edits
        </Text>
      </View>
    </ImageBackground>
  );
};

const getStyles = () =>
  StyleSheet.create({
    background: {
      color: 'white',
      flex: 1,
      flexDirection: 'column',
    },
    container: {
      flex: 6,
      flexDirection: 'row',
      alignItems: 'center',
    },
    headerContainer: {
      marginLeft: 200,
    },
    headerText: {
      color: 'white',
      fontSize: 80,
      marginBottom: 10,
    },
    subHeaderText: {
      color: 'white',
      fontSize: 40,
    },
    links: {
      flex: 1,
      flexDirection: 'column',
      justifyContent: 'space-around',
      height: 600,
    },
    image: {
      flex: 1,
      paddingLeft: 150,
    },
    textContainer: {
      justifyContent: 'center',
      flex: 1,
      marginLeft: 190,
    },
    text: {
      color: 'white',
      fontSize: 40,
    },
  });

Link.tsx

import React, {useState} from 'react';
import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native';

interface LinkProps {
  linkText: string;
  onPress: Function;
  testID?: string;
}

export const Link = ({linkText, onPress, testID}: LinkProps) => {
  const [focused, setFocused] = useState(false);

  return (
    <>
      <TouchableOpacity
        style={[styles.linkContainer, focused && styles.focusedContainer]}
        onPress={() => onPress()}
        onFocus={() => setFocused(true)}
        onBlur={() => setFocused(false)}
        testID={testID}>
        <View style={styles.linkTextContainer}>
          {focused ? (
            <Image source={require('../assets/focusedStar.png')} />
          ) : (
            <Image source={require('../assets/star.png')} />
          )}
          <Text style={styles.linkText}>{linkText}</Text>
        </View>
      </TouchableOpacity>
    </>
  );
};

const styles = StyleSheet.create({
  linkContainer: {
    width: 420,
    paddingBottom: 10,
    borderBottomWidth: 5,
    borderBottomColor: 'transparent',
  },
  focusedContainer: {
    borderBottomWidth: 5,
    borderBottomColor: '#ff9900',
  },
  linkTextContainer: {
    display: 'flex',
    alignItems: 'center',
    flexDirection: 'row',
    marginLeft: 200,
    width: 300,
  },
  linkText: {
    color: 'white',
    fontSize: 45,
    marginLeft: 30,
  },
});

Manifest.toml

schema-version = 1

[package]
title = "Basic UI React Native Application for project buttonrepro120"
version = "0.1.0"
id = "com.amazondeveloper.buttonrepro120"

[components]
[[components.interactive]]
id = "com.amazondeveloper.buttonrepro120.main"
runtime-module = "/com.amazon.kepler.keplerscript.runtime.loader_2@IKeplerScript_2_0"
launch-type = "singleton"
categories = ["com.amazon.category.main"]

Warm regards,
Ivy

Hi @Ivy_Mahajan ,

Thank you for the detailed response. The issue is resolved and it is working now.

1 Like

Thanks for letting me know, happy developing !

1 Like