Repository: Kong/kongponents
Pull request #2706: fix(k-input): typescript improvements for k-input
Author: login: jw-foss
Labels: name: released
Created: 2025-04-17T06:00:58Z | Merged: 2025-04-23T15:35:15Z

# Summary

[KM-1139]

TypeScript improvements for `KInput` component.

- [x] Preview at https://github.com/Kong/konnect-ui-apps/pull/6515
  - [x] CI passed

[KM-1139]: https://konghq.atlassian.net/browse/KM-1139?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


Diff:
diff --git a/src/components/KInput/KInput.vue b/src/components/KInput/KInput.vue
index ce334d3cf3..095346fad5 100644
--- a/src/components/KInput/KInput.vue
+++ b/src/components/KInput/KInput.vue
@@ -91,81 +91,39 @@
 
 <script setup lang="ts">
 import { computed, ref, watch, useSlots, useAttrs, onMounted, nextTick, useId } from 'vue'
-import type { PropType } from 'vue'
-import type { LabelAttributes, LimitExceededData } from '@/types'
+import type { InputProps, InputEmits, InputSlots } from '@/types'
 import useUtilities from '@/composables/useUtilities'
 import KLabel from '@/components/KLabel/KLabel.vue'
 import { KUI_ICON_SIZE_40 } from '@kong/design-tokens'
 import { VisibilityIcon, VisibilityOffIcon } from '@kong/icons'
 
-const props = defineProps({
-  modelValue: {
-    type: [String, Number],
-    default: '',
-  },
-  label: {
-    type: String,
-    default: '',
-  },
-  labelAttributes: {
-    type: Object as PropType<LabelAttributes>,
-    default: () => ({}),
-    validator: (value: LabelAttributes): boolean => {
-      if (value.help) {
-        console.warn('KInput: `help` property of `labelAttributes` prop is deprecated. Please use `info` prop instead. See the migration guide for more details: https://kongponents.konghq.com/guide/migrating-to-version-9.html#klabel')
-      }
-
-      return true
-    },
-  },
-  help: {
-    type: String,
-    default: '',
-  },
-  error: {
-    type: Boolean,
-    default: false,
-  },
-  errorMessage: {
-    type: String,
-    default: '',
-  },
-  characterLimit: {
-    type: Number,
-    default: null,
-    // Ensure the characterLimit is greater than zero
-    validator: (limit: number): boolean => limit > 0,
-  },
-  /**
-   * @deprecated in favor of `error`
-   */
-  hasError: {
-    type: Boolean,
-    default: false,
-    validator: (value: boolean): boolean => {
-      if (value) {
-        console.warn('KInput: `hasError` prop is deprecated. Please use `error` prop instead. See the migration guide for more details: https://kongponents.konghq.com/guide/migrating-to-version-9.html#kinput')
-      }
+const {
+  modelValue = '',
+  label = '',
+  labelAttributes = {},
+  help = '',
+  error,
+  errorMessage = '',
+  characterLimit = null,
+  hasError,
+  type = 'text',
+  showPasswordMaskToggle,
+} = defineProps<InputProps>()
+
+watch(() => hasError, (val) => {
+  if (val) {
+    console.warn('KInput: `hasError` prop is deprecated. Please use `error` prop instead. See the migration guide for more details: https://kongponents.konghq.com/guide/migrating-to-version-9.html#kinput')
+  }
+}, { immediate: true })
 
-      return true
-    },
-  },
-  type: {
-    type: String,
-    required: false,
-    default: 'text',
-  },
-  showPasswordMaskToggle: {
-    type: Boolean,
-    default: false,
-  },
+watch(() => labelAttributes.help, (help) => {
+  if (help) {
+    console.warn('KInput: `help` property of `labelAttributes` prop is deprecated. Please use `info` prop instead. See the migration guide for more details: https://kongponents.konghq.com/guide/migrating-to-version-9.html#klabel')
+  }
 })
 
-const emit = defineEmits<{
-  (e: 'input', val: string): void
-  (e: 'update:modelValue', val: string): void
-  (e: 'char-limit-exceeded', val: LimitExceededData): void
-}>()
+const emit = defineEmits<InputEmits>()
+defineSlots<InputSlots>()
 
 const currValue = ref<string>('') // We need this so that we don't lose the updated value on hover/blur event with label
 const modelValueChanged = ref<boolean>(false) // Determine if the original value was modified by the user
@@ -179,13 +137,13 @@ const isRequired = computed((): boolean => attrs?.required !== undefined && Stri
 const defaultId = useId()
 const inputId = computed((): string => attrs.id ? String(attrs.id) : defaultId)
 const helpTextId = useId()
-const strippedLabel = computed((): string => stripRequiredLabel(props.label, isRequired.value))
-const hasLabelTooltip = computed((): boolean => !!(props.labelAttributes?.info || slots['label-tooltip']))
+const strippedLabel = computed((): string => stripRequiredLabel(label, isRequired.value))
+const hasLabelTooltip = computed((): boolean => !!(labelAttributes?.info || slots['label-tooltip']))
 
 // we need this so we can create a watcher for programmatic changes to the modelValue
 const value = computed({
   get(): string | number {
-    return props.modelValue
+    return modelValue
   },
   set(newValue: string | number): void {
     // @ts-ignore: allow typing as Event
@@ -207,7 +165,7 @@ const modifiedAttrs = computed((): Record<string, any> => {
 
 const charLimitExceeded = computed((): boolean => {
   const currValLength = currValue.value?.toString().length || 0
-  const modelValLength = props.modelValue?.toString().length || 0
+  const modelValLength = modelValue?.toString().length || 0
 
   // default to length of currVal
   let length = currValLength
@@ -217,7 +175,7 @@ const charLimitExceeded = computed((): boolean => {
     length = modelValLength
   }
 
-  return !!props.characterLimit && length > props.characterLimit
+  return !!characterLimit && length > characterLimit
 })
 
 const charLimitExceededErrorMessage = computed((): string => {
@@ -226,8 +184,8 @@ const charLimitExceededErrorMessage = computed((): string => {
   }
 
   return modelValueChanged.value
-    ? `${currValue.value?.toString().length} / ${props.characterLimit}`
-    : `${props.modelValue?.toString().length} / ${props.characterLimit}`
+    ? `${currValue.value?.toString().length} / ${characterLimit}`
+    : `${modelValue?.toString().length} / ${characterLimit}`
 })
 
 const helpText = computed((): string => {
@@ -237,13 +195,13 @@ const helpText = computed((): string => {
   }
 
   // if error prop is true and there is an error message, return that
-  if ((props.error || props.hasError) && props.errorMessage) {
-    return props.errorMessage
+  if ((error || hasError) && errorMessage) {
+    return errorMessage
   }
 
   // otherwise return the help text
   // if error prop is true it danger styles will be applied
-  return props.help
+  return help
 })
 
 watch(charLimitExceeded, (newVal, oldVal) => {
@@ -251,7 +209,7 @@ watch(charLimitExceeded, (newVal, oldVal) => {
     emit('char-limit-exceeded', {
       value: currValue.value,
       length: currValue.value.length,
-      characterLimit: props.characterLimit,
+      characterLimit: characterLimit!,
       limitExceeded: newVal,
     })
 
@@ -284,10 +242,10 @@ const updateInputValue = (value: string): void => {
 
 const getValue = (): string | number => {
   // Use the modelValue only if it was initialized to something and the value hasn't been changed
-  return currValue.value || modelValueChanged.value ? currValue.value : props.modelValue
+  return currValue.value || modelValueChanged.value ? currValue.value : modelValue
 }
 
-watch(() => props.error, (newVal, oldVal) => {
+watch(() => error, (newVal, oldVal) => {
   if (newVal !== oldVal) {
     // bump the key to trigger the transition
     helpTextKey.value += 1
@@ -301,7 +259,7 @@ const afterSlotElementWidth = ref<string>(KUI_ICON_SIZE_40) // default to slot i
 
 const maskValue = ref<boolean>(false)
 const inputType = computed((): string => {
-  return props.type === 'password' && maskValue.value ? 'text' : props.type
+  return type === 'password' && maskValue.value ? 'text' : type
 })
 
 onMounted(async () => {
diff --git a/src/types/input.ts b/src/types/input.ts
index 6331e07fd3..8b25689515 100644
--- a/src/types/input.ts
+++ b/src/types/input.ts
@@ -8,6 +8,104 @@ export interface LabelAttributes {
   help?: string
 }
 
+export interface InputProps {
+  /**
+   * To set the value of the input element without using `v-model`.
+   * @defaults ''
+   */
+  modelValue?: string | number
+
+  /**
+   * String to be used as the input label.
+   * @defaults ''
+   */
+  label?: string
+
+  /**
+   * KCheckbox has an instance of KLabel for supporting tooltip text.
+   * Use the labelAttributes prop to configure the KLabel's props.
+   * @defaults {}
+   */
+  labelAttributes?: LabelAttributes
+
+  /**
+   * String to be displayed as help text.
+   * @defaults ''
+   */
+  help?: string
+
+  /**
+   * Boolean to indicate whether the element is in an error state and should apply error styling.
+   * @defaults false
+   */
+  error?: boolean
+
+  /**
+   * String to be displayed as an error message if `error` prop is `true`.
+   * This prop will supersede the `help` prop if both have a value and `error` is `true`.
+   * @defaults false
+   */
+  errorMessage?: string
+
+  /**
+   * Use this prop to specify a character limit for the input.
+   * See the @char-limit-exceeded event for more details.
+   * @defaults null
+   */
+  characterLimit?: number
+
+  /**
+   * @deprecated in favor of `error`
+   */
+  hasError?: boolean
+
+  /**
+   * HTML Input Element `type` attribute.
+   * @defaults 'text'
+   */
+  type?: string
+
+  /**
+   * When passing type="password", setting showPasswordMaskToggle to true
+   * will render an eye icon to the right of input clicking on which
+   * allows toggling masking the input value on and off.
+   * @defaults false
+   */
+  showPasswordMaskToggle?: boolean
+}
+
+export interface InputEmits {
+  /**
+   * To listen for changes to the KInput value.
+   */
+  input: [val: string]
+
+  /**
+   * To listen for changes to the KInput value, automatically set by `v-model`.
+   */
+  'update:modelValue': [val: string]
+
+  /**
+   * Fired when the text starts or stops exceeding the limit, returns an object @LimitExceededData.
+   */
+  'char-limit-exceeded': [val: LimitExceededData]
+}
+
+export interface InputSlots {
+  /**
+   * Inserting icons before the input field.
+   */
+  before?(): any
+  /**
+   * Inserting icons after the input field.
+   */
+  after?(): any
+  /**
+   * Slot for customizing the input label's tooltip.
+   */
+  'label-tooltip'?(): any
+}
+
 export interface LimitExceededData {
   value: string
   length: number


Reviews:
user: login: Justineo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-17T06:30:08Z
user: login: jw-foss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T06:36:55Z
user: login: jw-foss
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T06:39:30Z
user: login: adamdehaven
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-18T14:32:06Z
user: login: Justineo
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-22T07:30:31Z
user: login: adamdehaven
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T15:17:01Z
user: login: Justineo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T15:34:47Z
user: login: adamdehaven
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-23T15:35:14Z

Review comments:
diff_hunk: @@ -8,6 +8,104 @@ export interface LabelAttributes {
   help?: string
 }
 
+export interface InputProps {
+  /**
+   * To set the value of the input element without using `v-model`.
+   * @defaults ''
+   */
+  modelValue?: string | number
+
+  /**
+   * String to be used as the input label.
+   * @defaults ''
+   */
+  label?: string
+
+  /**
+   * KCheckbox has an instance of KLabel for supporting tooltip text.
+   * Use the labelAttributes prop to configure the KLabel's props.
+   * @defaults {}
+   */
+  labelAttributes?: LabelAttributes
+
+  /**
+   * String to be displayed as help text.
+   * @defaults ''
+   */
+  help?: string
+
+  /**
+   * Boolean to indicate whether the element is in an error state and should apply error styling.
+   * @defaults false
+   */
+  error?: boolean
+
+  /**
+   * String to be displayed as an error message if `error` prop is `true`.
+   * This prop will supersede the `help` prop if both have a value and `error` is `true`.
+   * @defaults false
+   */
+  errorMessage?: string
+
+  /**
+   * Use this prop to specify a character limit for the input.
+   * See the @char-limit-exceeded event for more details.
+   * @defaults null
+   */
+  characterLimit?: number
path: src/types/input.ts
user: login: Justineo
body: Shall we remove the default value `null` for this one? Otherwise it should be `number | null` here.
created_at: 2025-04-17T06:29:21Z
author_association: MEMBER
diff_hunk: @@ -237,21 +195,21 @@ const helpText = computed((): string => {
   }
 
   // if error prop is true and there is an error message, return that
-  if ((props.error || props.hasError) && props.errorMessage) {
-    return props.errorMessage
+  if ((error || hasError) && errorMessage) {
+    return errorMessage
   }
 
   // otherwise return the help text
   // if error prop is true it danger styles will be applied
-  return props.help
+  return help
 })
 
 watch(charLimitExceeded, (newVal, oldVal) => {
   if (newVal !== oldVal) {
     emit('char-limit-exceeded', {
       value: currValue.value,
       length: currValue.value.length,
-      characterLimit: props.characterLimit,
+      characterLimit: characterLimit!,
path: src/components/KInput/KInput.vue
user: login: Justineo
body: Could this be `undefined` or `null`?
created_at: 2025-04-17T06:30:05Z
author_association: MEMBER
diff_hunk: @@ -237,21 +195,21 @@ const helpText = computed((): string => {
   }
 
   // if error prop is true and there is an error message, return that
-  if ((props.error || props.hasError) && props.errorMessage) {
-    return props.errorMessage
+  if ((error || hasError) && errorMessage) {
+    return errorMessage
   }
 
   // otherwise return the help text
   // if error prop is true it danger styles will be applied
-  return props.help
+  return help
 })
 
 watch(charLimitExceeded, (newVal, oldVal) => {
   if (newVal !== oldVal) {
     emit('char-limit-exceeded', {
       value: currValue.value,
       length: currValue.value.length,
-      characterLimit: props.characterLimit,
+      characterLimit: characterLimit!,
path: src/components/KInput/KInput.vue
user: login: jw-foss
body: @Justineo could be, you can see in the screenshot, if the value `characterLimit` is not a valid value, this watcher will not be triggered.
![CleanShot 2025-04-17 at 14 36 13@2x](https://github.com/user-attachments/assets/31fcad1b-536f-4bfb-a291-a5ae7870e686)

created_at: 2025-04-17T06:36:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,6 +8,104 @@ export interface LabelAttributes {
   help?: string
 }
 
+export interface InputProps {
+  /**
+   * To set the value of the input element without using `v-model`.
+   * @defaults ''
+   */
+  modelValue?: string | number
+
+  /**
+   * String to be used as the input label.
+   * @defaults ''
+   */
+  label?: string
+
+  /**
+   * KCheckbox has an instance of KLabel for supporting tooltip text.
+   * Use the labelAttributes prop to configure the KLabel's props.
+   * @defaults {}
+   */
+  labelAttributes?: LabelAttributes
+
+  /**
+   * String to be displayed as help text.
+   * @defaults ''
+   */
+  help?: string
+
+  /**
+   * Boolean to indicate whether the element is in an error state and should apply error styling.
+   * @defaults false
+   */
+  error?: boolean
+
+  /**
+   * String to be displayed as an error message if `error` prop is `true`.
+   * This prop will supersede the `help` prop if both have a value and `error` is `true`.
+   * @defaults false
+   */
+  errorMessage?: string
+
+  /**
+   * Use this prop to specify a character limit for the input.
+   * See the @char-limit-exceeded event for more details.
+   * @defaults null
+   */
+  characterLimit?: number
path: src/types/input.ts
user: login: jw-foss
body: IMHO, the characterLimit default value should be `0`, when it is `0`, meaning that the limit is `Infinite` or maybe we should use `Number.Infinity` here instead. But since `null` has been used here for this matter, maybe it is safer to keep it this way.
created_at: 2025-04-17T06:39:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -91,81 +91,39 @@
 
 <script setup lang="ts">
 import { computed, ref, watch, useSlots, useAttrs, onMounted, nextTick, useId } from 'vue'
-import type { PropType } from 'vue'
-import type { LabelAttributes, LimitExceededData } from '@/types'
+import type { InputProps, InputEmits, InputSlots } from '@/types'
 import useUtilities from '@/composables/useUtilities'
 import KLabel from '@/components/KLabel/KLabel.vue'
 import { KUI_ICON_SIZE_40 } from '@kong/design-tokens'
 import { VisibilityIcon, VisibilityOffIcon } from '@kong/icons'
 
-const props = defineProps({
-  modelValue: {
-    type: [String, Number],
-    default: '',
-  },
-  label: {
-    type: String,
-    default: '',
-  },
-  labelAttributes: {
-    type: Object as PropType<LabelAttributes>,
-    default: () => ({}),
-    validator: (value: LabelAttributes): boolean => {
-      if (value.help) {
-        console.warn('KInput: `help` property of `labelAttributes` prop is deprecated. Please use `info` prop instead. See the migration guide for more details: https://kongponents.konghq.com/guide/migrating-to-version-9.html#klabel')
-      }
-
-      return true
-    },
-  },
-  help: {
-    type: String,
-    default: '',
-  },
-  error: {
-    type: Boolean,
-    default: false,
-  },
-  errorMessage: {
-    type: String,
-    default: '',
-  },
-  characterLimit: {
-    type: Number,
-    default: null,
-    // Ensure the characterLimit is greater than zero
-    validator: (limit: number): boolean => limit > 0,
-  },
-  /**
-   * @deprecated in favor of `error`
-   */
-  hasError: {
-    type: Boolean,
-    default: false,
-    validator: (value: boolean): boolean => {
-      if (value) {
-        console.warn('KInput: `hasError` prop is deprecated. Please use `error` prop instead. See the migration guide for more details: https://kongponents.konghq.com/guide/migrating-to-version-9.html#kinput')
-      }
+const {
+  modelValue = '',
+  label = '',
+  labelAttributes = {},
+  help = '',
+  error = false,
+  errorMessage = '',
+  characterLimit = null,
+  hasError = false,
+  type = 'text',
+  showPasswordMaskToggle = false,
path: src/components/KInput/KInput.vue
user: login: adamdehaven
body: to be like the other PRs, we shouldn't need to define the `false` prop defaults here for boolean props, right?
created_at: 2025-04-18T14:31:42Z
author_association: MEMBER
diff_hunk: @@ -91,81 +91,39 @@
 
 <script setup lang="ts">
 import { computed, ref, watch, useSlots, useAttrs, onMounted, nextTick, useId } from 'vue'
-import type { PropType } from 'vue'
-import type { LabelAttributes, LimitExceededData } from '@/types'
+import type { InputProps, InputEmits, InputSlots } from '@/types'
 import useUtilities from '@/composables/useUtilities'
 import KLabel from '@/components/KLabel/KLabel.vue'
 import { KUI_ICON_SIZE_40 } from '@kong/design-tokens'
 import { VisibilityIcon, VisibilityOffIcon } from '@kong/icons'
 
-const props = defineProps({
-  modelValue: {
-    type: [String, Number],
-    default: '',
-  },
-  label: {
-    type: String,
-    default: '',
-  },
-  labelAttributes: {
-    type: Object as PropType<LabelAttributes>,
-    default: () => ({}),
-    validator: (value: LabelAttributes): boolean => {
-      if (value.help) {
-        console.warn('KInput: `help` property of `labelAttributes` prop is deprecated. Please use `info` prop instead. See the migration guide for more details: https://kongponents.konghq.com/guide/migrating-to-version-9.html#klabel')
-      }
-
-      return true
-    },
-  },
-  help: {
-    type: String,
-    default: '',
-  },
-  error: {
-    type: Boolean,
-    default: false,
-  },
-  errorMessage: {
-    type: String,
-    default: '',
-  },
-  characterLimit: {
-    type: Number,
-    default: null,
-    // Ensure the characterLimit is greater than zero
-    validator: (limit: number): boolean => limit > 0,
-  },
-  /**
-   * @deprecated in favor of `error`
-   */
-  hasError: {
-    type: Boolean,
-    default: false,
-    validator: (value: boolean): boolean => {
-      if (value) {
-        console.warn('KInput: `hasError` prop is deprecated. Please use `error` prop instead. See the migration guide for more details: https://kongponents.konghq.com/guide/migrating-to-version-9.html#kinput')
-      }
+const {
+  modelValue = '',
path: src/components/KInput/KInput.vue
user: login: adamdehaven
body: question: why not refactor to utilize `defineModel`?
created_at: 2025-04-23T15:15:43Z
author_association: MEMBER
diff_hunk: @@ -91,81 +91,39 @@
 
 <script setup lang="ts">
 import { computed, ref, watch, useSlots, useAttrs, onMounted, nextTick, useId } from 'vue'
-import type { PropType } from 'vue'
-import type { LabelAttributes, LimitExceededData } from '@/types'
+import type { InputProps, InputEmits, InputSlots } from '@/types'
 import useUtilities from '@/composables/useUtilities'
 import KLabel from '@/components/KLabel/KLabel.vue'
 import { KUI_ICON_SIZE_40 } from '@kong/design-tokens'
 import { VisibilityIcon, VisibilityOffIcon } from '@kong/icons'
 
-const props = defineProps({
-  modelValue: {
-    type: [String, Number],
-    default: '',
-  },
-  label: {
-    type: String,
-    default: '',
-  },
-  labelAttributes: {
-    type: Object as PropType<LabelAttributes>,
-    default: () => ({}),
-    validator: (value: LabelAttributes): boolean => {
-      if (value.help) {
-        console.warn('KInput: `help` property of `labelAttributes` prop is deprecated. Please use `info` prop instead. See the migration guide for more details: https://kongponents.konghq.com/guide/migrating-to-version-9.html#klabel')
-      }
-
-      return true
-    },
-  },
-  help: {
-    type: String,
-    default: '',
-  },
-  error: {
-    type: Boolean,
-    default: false,
-  },
-  errorMessage: {
-    type: String,
-    default: '',
-  },
-  characterLimit: {
-    type: Number,
-    default: null,
-    // Ensure the characterLimit is greater than zero
-    validator: (limit: number): boolean => limit > 0,
-  },
-  /**
-   * @deprecated in favor of `error`
-   */
-  hasError: {
-    type: Boolean,
-    default: false,
-    validator: (value: boolean): boolean => {
-      if (value) {
-        console.warn('KInput: `hasError` prop is deprecated. Please use `error` prop instead. See the migration guide for more details: https://kongponents.konghq.com/guide/migrating-to-version-9.html#kinput')
-      }
+const {
+  modelValue = '',
path: src/components/KInput/KInput.vue
user: login: Justineo
body: Migrating to `defineModel` would require a bit more refactoring than our current changes. Given our current priorities, I think it's okay to keep the changes minimal for this round.
created_at: 2025-04-23T15:34:46Z
author_association: MEMBER

PR comments:
user: login: netlify[bot]
created_at: 2025-04-17T06:01:18Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *kongponents-sandbox* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 9c7e4c6cf07e919766adeb4705ae0187cc5adfa1 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/kongponents-sandbox/deploys/6805abd1dcb88900078392b6 |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-2706--kongponents-sandbox.netlify.app](https://deploy-preview-2706--kongponents-sandbox.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTI3MDYtLWtvbmdwb25lbnRzLXNhbmRib3gubmV0bGlmeS5hcHAifQ.U6LaqBY8tkyJlaaZoVO6m-flsZXynn3RZBh0kG5jxFo)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [kongponents-sandbox Preview](https://deploy-preview-2706--kongponents-sandbox.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/kongponents-sandbox/configuration/notifications#deploy-webhooks)._
author_association: NONE
user: login: netlify[bot]
created_at: 2025-04-17T06:03:08Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *kongponents* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 9c7e4c6cf07e919766adeb4705ae0187cc5adfa1 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/kongponents/deploys/6805abd17705220008e30a10 |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-2706--kongponents.netlify.app](https://deploy-preview-2706--kongponents.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTI3MDYtLWtvbmdwb25lbnRzLm5ldGxpZnkuYXBwIn0.M6pMBj_mG3uTv2bEBY-8fnZ5r6Q7XMHe-P7QbZgmNb4)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [kongponents Preview](https://deploy-preview-2706--kongponents.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/kongponents/configuration/notifications#deploy-webhooks)._
author_association: NONE
user: login: kongponents-bot
created_at: 2025-04-23T15:42:23Z
body: :tada: This PR is included in version 9.29.4 :tada:

The release is available on:
- [npm package (@latest dist-tag)](https://www.npmjs.com/package/@kong/kongponents/v/9.29.4)
- [GitHub release](https://github.com/Kong/kongponents/releases/tag/v9.29.4)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: COLLABORATOR