Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions frontend/common/utils/__tests__/featureValueValidation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
getFeatureValueLengthError,
hasInvalidFeatureValueLength,
} from 'common/utils/featureValueValidation'

describe('getFeatureValueLengthError', () => {
it('accepts values up to the configured maximum', () => {
expect(getFeatureValueLengthError('x'.repeat(20_000))).toBeUndefined()
})

it('reports the actual and maximum character counts when exceeded', () => {
expect(getFeatureValueLengthError('x'.repeat(20_001))).toBe(
'Flag value is 20,001 characters. Maximum length is 20,000 characters.',
)
})

it('counts Unicode code points as characters', () => {
expect(getFeatureValueLengthError('😀'.repeat(20_001))).toContain(
'20,001 characters',
)
})

it('does not apply the string limit to non-string values', () => {
expect(getFeatureValueLengthError(20_001)).toBeUndefined()
})

it('detects any over-limit value in a save', () => {
expect(hasInvalidFeatureValueLength(['valid', 'x'.repeat(20_001)])).toBe(
true,
)
expect(hasInvalidFeatureValueLength(['valid', 'also valid'])).toBe(false)
})
})
Comment on lines +1 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivial · Maintainability & Code Quality · Heavy lift

Test value-length validation through the save paths.

Add integration tests for oversized control, variant and segment values during flag creation, direct saves and change requests. Assert that saving is blocked and the editor shows an alert. Also assert that saving succeeds when the only oversized segment override is marked for removal.

These tests would protect the save guard and removal exception, which the utility tests do not exercise.

31 changes: 31 additions & 0 deletions frontend/common/utils/featureValueValidation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export const MAX_FEATURE_VALUE_LENGTH = 20_000

export function getFeatureValueLengthError(
value: unknown,
label = 'Flag value',
maxLength = MAX_FEATURE_VALUE_LENGTH,
): string | undefined {
if (typeof value !== 'string') return undefined

const characterCount = Array.from(value).length
if (characterCount <= maxLength) return undefined

return `${label} is ${characterCount.toLocaleString(
'en-GB',
)} characters. Maximum length is ${maxLength.toLocaleString(
'en-GB',
)} characters.`
}

export function getFeatureValueLengthErrors(
values: { label: string; value: unknown }[],
): string[] {
return values.flatMap(({ label, value }) => {
const error = getFeatureValueLengthError(value, label)
return error ? [error] : []
})
}

export function hasInvalidFeatureValueLength(values: unknown[]): boolean {
return values.some((value) => !!getFeatureValueLengthError(value))
}
2 changes: 2 additions & 0 deletions frontend/web/components/SegmentOverrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ const SegmentOverrideInner = class Override extends React.Component {
<div className='flex-fill overflow-hidden'>
<ValueEditor
label='Value'
validateLength
disabled={readOnly}
value={v.value}
onChange={
Expand All @@ -295,6 +296,7 @@ const SegmentOverrideInner = class Override extends React.Component {
<div className='flex-1 flex-column'>
<ValueEditor
label='Segment Control Value'
validateLength
labelAfter={<ControlWeightChip percentage={controlPercent} />}
value={v.value}
disabled={readOnly}
Expand Down
22 changes: 21 additions & 1 deletion frontend/web/components/ValueEditor/ValueEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ import React, {
} from 'react'
import cx from 'classnames'

import { FlagsmithValue } from 'common/types/responses'
import {
getFeatureValueLengthError,
MAX_FEATURE_VALUE_LENGTH,
} from 'common/utils/featureValueValidation'
import ErrorMessage from 'components/ErrorMessage'
import FieldLabel from 'components/base/forms/FieldLabel'
import Highlight from 'components/Highlight'
import { FlagsmithValue } from 'common/types/responses'

import CopyValueButton from './components/CopyValueButton'
import LanguageSelector from './components/LanguageSelector'
Expand All @@ -36,6 +41,7 @@ export interface ValueEditorProps {
// caller's job (Utils.getTypedValue, Utils.valueToFeatureState).
onChange?: (value: string) => void
value?: FlagsmithValue
validateLength?: boolean
}

const ValueEditor: FC<ValueEditorProps> = ({
Expand All @@ -47,6 +53,7 @@ const ValueEditor: FC<ValueEditorProps> = ({
language: languageProp,
onBlur,
onChange,
validateLength = false,
value,
}) => {
const [picked, setPicked] = useState<ValueEditorLanguage>()
Expand All @@ -56,6 +63,13 @@ const ValueEditor: FC<ValueEditorProps> = ({
const language = languageProp ?? picked ?? detected ?? 'txt'
const labelId = useId()
const text = value === undefined || value === null ? '' : `${value}`
const lengthError = validateLength
? getFeatureValueLengthError(
text,
typeof label === 'string' ? label : 'Value',
MAX_FEATURE_VALUE_LENGTH,
)
: undefined
Comment on lines +66 to +72

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Performance & Scalability · Low value

Supported by static analysis

Script executed:

git diff 13539afcb99c20b4351e26f215d01b49f2a9be03 85c5f6566801e363eeb79fbb6b55ee9f838d393b -- frontend/web/components/ValueEditor/ValueEditor.tsx frontend/common/utils/featureValueValidation.ts
nl -ba frontend/web/components/ValueEditor/ValueEditor.tsx | sed -n '1,230p'
nl -ba frontend/common/utils/featureValueValidation.ts
rg -n -F -e 'validateLength' -e 'ValueEditor' -- frontend/web/components/SegmentOverrides.js frontend/web/components/modals/create-feature frontend/web/components/mv/VariationValueInput frontend/web/components/ValueEditor

Repository: Flagsmith/flagsmith

Length of output: 15370


Script executed:

rg --files frontend/web/components -g '*Highlight*'
rg -n -F -e 'VariationValueInput' -e 'SegmentOverrides' -e 'onChange' -- frontend/web/components/mv/VariationValueInput/VariationValueInput.tsx frontend/web/components/SegmentOverrides.js frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx
nl -ba frontend/web/components/mv/VariationValueInput/VariationValueInput.tsx
nl -ba frontend/web/components/SegmentOverrides.js | sed -n '1,340p'
nl -ba frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx | sed -n '350,435p'
rg -n -F -e 'VariationValueInput' -e 'SegmentOverrides' -- frontend/web/components/mv frontend/web/components/modals/create-feature

Repository: Flagsmith/flagsmith

Length of output: 29163


Script executed:

nl -ba frontend/web/components/Highlight.js
nl -ba frontend/web/components/mv/VariationOptions.tsx | sed -n '1,260p'
nl -ba frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx | sed -n '550,630p'
rg -n -F -e 'MAX_FEATURE_VALUE_LENGTH' -e 'maxLength' -e 'max_length' -- frontend/web/components/Highlight.js frontend/web/components/mv frontend/web/components/ValueEditor frontend/common/utils/featureValueValidation.ts
rg -n -F -e 'onEnvironmentFlagChange' -e 'setVariations' -- frontend/web/components/modals/create-feature/index.tsx frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx frontend/web/components/mv/VariationOptions.tsx

Repository: Flagsmith/flagsmith

Length of output: 23105


Script executed:

nl -ba frontend/web/components/modals/create-feature/index.tsx | sed -n '845,910p'
nl -ba frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx | sed -n '115,235p'
nl -ba frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx | sed -n '510,558p'
rg -n -F -e 'updateVariation=' -e 'updateVariation:' -e 'handleUpdateVariation' -e 'setEnvironmentFlag' -- frontend/web/components/modals/create-feature

Repository: Flagsmith/flagsmith

Length of output: 11851


Script executed:

rg -n -F -e 'AddVariationButton' -e 'MAX_VARIATIONS' -e 'max_variations' -e 'MAX_MULTIVARIATE' -- frontend/web/components frontend/common | sed -n '1,140p'
rg --files frontend/web/components -g '*AddVariationButton*'

Repository: Flagsmith/flagsmith

Length of output: 785


Script executed:

nl -ba frontend/web/components/mv/AddVariationButton.tsx

Repository: Flagsmith/flagsmith

Length of output: 1063


Script executed:

nl -ba frontend/web/components/modals/create-feature/index.tsx | sed -n '320,405p'
nl -ba frontend/web/components/modals/create-feature/index.tsx | sed -n '480,520p'
nl -ba frontend/web/components/modals/create-feature/index.tsx | sed -n '590,685p'
rg -n -F -e 'getFeatureValueLengthErrors' -e 'hasInvalidFeatureValueLength' -e 'validateValueLengths' -- frontend/web/components/modals/create-feature/index.tsx frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx

Repository: Flagsmith/flagsmith

Length of output: 10558


Script executed:

rg -n -F -e 'valueToFeatureState' -e 'featureStateToValue' -e 'getTypedValue' -- frontend/common/utils/utils.js frontend/common/utils/utils.ts

Repository: Flagsmith/flagsmith

Length of output: 307


Script executed:

rg --files frontend/common/utils | rg '(^|/)utils(/|\.|$)'
rg -n -F -e 'valueToFeatureState' -e 'featureStateToValue' -e 'getTypedValue' -- frontend/common/utils

Repository: Flagsmith/flagsmith

Length of output: 4101


Script executed:

nl -ba frontend/common/utils/utils.tsx | sed -n '575,645p;825,865p'
nl -ba frontend/common/utils/featureStateToValue.ts

Repository: Flagsmith/flagsmith

Length of output: 5457


Memoise the length check for unchanged editors.

When many variants have large values, editing one variant recounts every unchanged value and allocates an array for each. This can cause typing lag. Memoise the check so unchanged editors reuse their results.

Suggested fix

--- "a/frontend/web/components/ValueEditor/ValueEditor.tsx"
+++ "b/frontend/web/components/ValueEditor/ValueEditor.tsx"
@@ -63,13 +63,17 @@
   const language = languageProp ?? picked ?? detected ?? 'txt'
   const labelId = useId()
   const text = value === undefined || value === null ? '' : `${value}`
-  const lengthError = validateLength
-    ? getFeatureValueLengthError(
-        text,
-        typeof label === 'string' ? label : 'Value',
-        MAX_FEATURE_VALUE_LENGTH,
-      )
-    : undefined
+  const lengthError = useMemo(
+    () =>
+      validateLength
+        ? getFeatureValueLengthError(
+            text,
+            typeof label === 'string' ? label : 'Value',
+            MAX_FEATURE_VALUE_LENGTH,
+          )
+        : undefined,
+    [text, label, validateLength],
+  )
 
   // Detection waits for a value rather than running on mount: values load
   // after mount, and a mount-only check left JSON rendering as plaintext.
Committable suggestion

IMPORTANT
Review the replacement and indentation, then test before committing.

Suggested change
const lengthError = validateLength
? getFeatureValueLengthError(
text,
typeof label === 'string' ? label : 'Value',
MAX_FEATURE_VALUE_LENGTH,
)
: undefined
const lengthError = useMemo(
() =>
validateLength
? getFeatureValueLengthError(
text,
typeof label === 'string' ? label : 'Value',
MAX_FEATURE_VALUE_LENGTH,
)
: undefined,
[text, label, validateLength],
)


// Detection waits for a value rather than running on mount: values load
// after mount, and a mount-only check left JSON rendering as plaintext.
Expand Down Expand Up @@ -112,12 +126,18 @@ const ValueEditor: FC<ValueEditorProps> = ({
</div>
)}

{!!lengthError && (
<div role='alert' className='mb-2'>
<ErrorMessage error={lengthError} />
</div>
)}
<div className='value-editor__field position-relative'>
{showControls && <CopyValueButton value={text} />}

<Highlight
aria-labelledby={label ? labelId : undefined}
aria-readonly={disabled || undefined}
aria-invalid={!!lengthError || undefined}
disabled={disabled}
onChange={disabled ? null : onChange}
Comment on lines 137 to 142

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Low value

Supported by static analysis

Script executed:

git diff 13539afcb99c20b4351e26f215d01b49f2a9be03 85c5f6566801e363eeb79fbb6b55ee9f838d393b -- frontend/web/components/ValueEditor/ValueEditor.tsx
nl -ba frontend/web/components/ValueEditor/ValueEditor.tsx | sed -n '1,220p'
rg -n -F -e 'class Highlight' -e 'function Highlight' -e 'const Highlight' -e 'from '\"'\"'components/Highlight'\"'\"'' -- frontend/web frontend/common || test "$?" -eq 1

Repository: Flagsmith/flagsmith

Length of output: 8581


Script executed:

nl -ba frontend/web/components/Highlight.js | sed -n '1,260p'
nl -ba frontend/common/utils/featureValueValidation.ts | sed -n '1,140p'
rg -n -C 5 -F -- 'validateLength' frontend/web/components/SegmentOverrides.js frontend/web/components/modals/create-feature frontend/web/components/mv/VariationValueInput frontend/web/components/ValueEditor
git diff 13539afcb99c20b4351e26f215d01b49f2a9be03 85c5f6566801e363eeb79fbb6b55ee9f838d393b -- frontend/web/components/Highlight.js

Repository: Flagsmith/flagsmith

Length of output: 16366


Forward the invalid state to the editable textbox.

When a value exceeds the limit, Highlight drops aria-invalid, so the textbox does not expose its invalid state to assistive technology. Forward the prop to the <code> element and include it in shouldComponentUpdate so the state updates during editing.

onBlur={disabled ? null : onBlur}
Expand Down
50 changes: 48 additions & 2 deletions frontend/web/components/modals/create-feature/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
import { saveFeatureWithValidation } from 'components/saveFeatureWithValidation'
import FeatureHistory from 'components/FeatureHistory'
import { getChangeRequests } from 'common/services/useChangeRequest'
import { getFeatureValueLengthErrors } from 'common/utils/featureValueValidation'
import FeatureHealthTabContent from 'components/feature-health/FeatureHealthTabContent'
import FeaturePipelineStatus from 'components/release-pipelines/FeaturePipelineStatus'
import { History } from 'history'
Expand Down Expand Up @@ -350,7 +351,34 @@ const CreateFeatureModal: FC<CreateFeatureModalProps> = (props) => {
return { featureError, featureWarning }
}

const getValueLengthErrors = () =>
getFeatureValueLengthErrors([
{
label: identity ? 'Identity override' : 'Flag value',
value: environmentFlag.feature_state_value,
},
...(projectFlag.multivariate_options || []).map(
(option: any, index: number) => ({
label: `Variant ${option.key || index + 1} value`,
value: option.string_value,
}),
),
...(segmentOverrides || [])
.filter((override: any) => !override.toRemove)
.map((override: any, index: number) => ({
label: `Segment override ${override.segment_name || index + 1} value`,
value: override.value,
})),
])
Comment on lines +354 to +372

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Low value

Supported by static analysis

Script executed:

sed -n '320,440p' frontend/web/components/modals/create-feature/index.tsx
sed -n '670,780p' frontend/web/components/modals/create-feature/index.tsx

Repository: Flagsmith/flagsmith

Length of output: 9206


Script executed:

printf '%s\n' '--- validation and save callers ---'
rg -n -F -e 'getValueLengthErrors' -e 'showValueLengthErrors' -e 'saveFeatureWithValidation' -e 'cleanInputValue' -e 'valueToFeatureState' -- frontend/web/components/modals/create-feature/index.tsx frontend/common/utils/utils.tsx
printf '%s\n' '--- save and validation source ---'
nl -ba frontend/web/components/modals/create-feature/index.tsx | sed -n '320,485p;590,790p;855,895p'
printf '%s\n' '--- conversion implementation ---'
nl -ba frontend/common/utils/utils.tsx | sed -n '580,630p;825,870p'
printf '%s\n' '--- validator ---'
nl -ba frontend/common/utils/featureValueValidation.ts

Repository: Flagsmith/flagsmith

Length of output: 25144


Script executed:

printf '%s\n' '--- wrapper, save branches and change-request callback ---'
nl -ba frontend/web/components/modals/create-feature/index.tsx | sed -n '495,535p;760,850p'
printf '%s\n' '--- editor state producer ---'
nl -ba frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx | sed -n '395,435p'
printf '%s\n' '--- payload consumer locations ---'
rg -n -F -e 'createChangeRequest(' -e 'editFeatureValue(' -e 'valueToFeatureState(' -- frontend/common/stores/feature-list-store.ts frontend/common/providers/FeatureListProvider.js frontend/web/components/modals/change-request frontend/common/services/useChangeRequest.ts || test "$?" -eq 1
printf '%s\n' '--- backend limits ---'
nl -ba api/features/feature_states/models.py | sed -n '20,45p'
nl -ba api/features/multivariate/models.py | sed -n '25,42p'

Repository: Flagsmith/flagsmith

Length of output: 9739


Script executed:

printf '%s\n' '--- change request bindings ---'
rg -n -F -e 'createChangeRequest' -e 'editFeatureValue' -e 'featureStates=' -- frontend/common/providers/FeatureListProvider.js frontend/common/stores/feature-list-store.ts frontend/web/components/modals/create-feature/index.tsx
rg -n -F -e 'function ChangeRequestModal' -e 'const ChangeRequestModal' -e 'class ChangeRequestModal' -- frontend/web/components frontend/common || test "$?" -eq 1
printf '%s\n' '--- provider callbacks ---'
nl -ba frontend/common/providers/FeatureListProvider.js | sed -n '1,180p'
printf '%s\n' '--- store candidate payloads ---'
nl -ba frontend/common/stores/feature-list-store.ts | sed -n '290,370p;410,485p;545,615p;615,680p'
printf '%s\n' '--- save wrapper and UI state validation ---'
rg -n -F -e 'validateLength' -e 'hasInvalidFeatureValueLength' -- frontend/web/components/ValueEditor/ValueEditor.tsx frontend/web/components/saveFeatureWithValidation* frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx || test "$?" -eq 1

Repository: Flagsmith/flagsmith

Length of output: 21822


Script executed:

printf '%s\n' '--- change request provider and store ownership ---'
nl -ba frontend/common/providers/FeatureListProvider.js | sed -n '185,240p'
nl -ba frontend/common/stores/feature-list-store.ts | sed -n '510,545p;570,595p;640,667p'
printf '%s\n' '--- change request modal use of featureStates ---'
rg -n -F -e 'featureStates' -e 'onSave' -e 'no changes' -- frontend/web/components/modals/ChangeRequestModal.tsx
nl -ba frontend/web/components/modals/ChangeRequestModal.tsx | sed -n '1,220p'
printf '%s\n' '--- frontend validation mechanisms ---'
nl -ba frontend/web/components/ValueEditor/ValueEditor.tsx | sed -n '45,90p;115,150p'
rg --files frontend/web/components -g '*saveFeatureWithValidation*'
rg -n -F -e 'validateValueLengths(' -e 'showValueLengthErrors(' -- frontend/web/components/modals/create-feature/index.tsx

Repository: Flagsmith/flagsmith

Length of output: 18376


Script executed:

printf '%s\n' '--- modal conflict-check invocation ---'
nl -ba frontend/web/components/modals/ChangeRequestModal.tsx | sed -n '275,330p'
printf '%s\n' '--- conflict-check source ---'
rg --files frontend/web/components -g '*ChangeRequestConflictCheck*'
rg -n -F -e 'featureStates' -e 'onChange' -e 'hasChanges' -e 'getFeatureStateCrud' -- frontend/web/components/ChangeRequestConflictCheck*
printf '%s\n' '--- complete component ---'
cat -n frontend/web/components/ChangeRequestConflictCheck.tsx
printf '%s\n' '--- save-with-validation helper ---'
cat -n frontend/web/components/saveFeatureWithValidation.ts

Repository: Flagsmith/flagsmith

Length of output: 7688


Script executed:

printf '%s\n' '--- change comparison hook ---'
cat -n frontend/common/hooks/useHasFeatureStateChanges.ts
printf '%s\n' '--- related comparison bindings ---'
rg -n -F -e 'getFeatureStateCrud' -e 'hasFeatureStateChanges' -- frontend/common/hooks/useHasFeatureStateChanges.ts frontend/common/utils

Repository: Flagsmith/flagsmith

Length of output: 3250


Script executed:

printf '%s\n' '--- comparison implementation ---'
rg -n -F -e 'getFeatureStateCrud' -e 'feature_state_value' -- frontend/common/services/useFeatureVersion.ts
nl -ba frontend/common/services/useFeatureVersion.ts | sed -n '1,240p'
printf '%s\n' '--- change-request dispatch binding ---'
rg -n -F -e 'editEnvironmentFlagChangeRequest' -e 'editFeatureStateChangeRequest' -- frontend/common/dispatcher/app-actions.js frontend/common/stores/feature-list-store.ts
printf '%s\n' '--- identity provider payload ---'
rg -n -F -e 'identityFlag' -e 'editFeatureValue' -- frontend/common/providers/IdentityProvider.js

Repository: Flagsmith/flagsmith

Length of output: 10846


Script executed:

printf '%s\n' '--- diff implementation ---'
rg -n -F -e 'getFeatureStateDiff' -e 'string_value' -- frontend/web/components/diff/diff-utils*
nl -ba frontend/web/components/diff/diff-utils.ts | sed -n '1,180p'
printf '%s\n' '--- dispatch binding ---'
nl -ba frontend/common/dispatcher/app-actions.js | sed -n '120,145p'
nl -ba frontend/common/stores/feature-list-store.ts | sed -n '1055,1095p'

Repository: Flagsmith/flagsmith

Length of output: 8719


Validate the flag value sent by save

A flag value with 20,000 characters followed by a space is rejected, although direct save trims it. Change-request payloads also use the trimmed value. Use that value for the modal’s change comparison as well.

Suggested fix

-        value: environmentFlag.feature_state_value,
+        value: cleanInputValue(environmentFlag.feature_state_value),
                   feature_state_value: Utils.valueToFeatureState(
-                    environmentFlag.feature_state_value,
+                    cleanInputValue(environmentFlag.feature_state_value),
                   ),


const showValueLengthErrors = (errors = getValueLengthErrors()) => {
errors.forEach((error) => toast(error, 'danger'))
return errors.length > 0
}

const save = (func: (...args: any[]) => void, isSaving: boolean) => {
if (showValueLengthErrors()) return
Comment on lines +374 to +380

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Quick win

Supported by static analysis

Script executed:

git diff 13539afcb99c20b4351e26f215d01b49f2a9be03 85c5f6566801e363eeb79fbb6b55ee9f838d393b -- frontend/web/components/modals/create-feature/index.tsx frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx frontend/common/utils/featureValueValidation.ts
rg -n -F -e 'showValueLengthErrors' -e 'validateValueLengths' -e 'saveFeatureValue' -e 'saveFeatureSegments' -e 'IdentitySaveFooter' -e 'onCreateFeature' -e 'toast(' frontend/web/components/modals/create-feature/index.tsx frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx
nl -ba frontend/web/components/modals/create-feature/index.tsx | sed -n '300,410p;460,535p;580,700p;830,910p'
nl -ba frontend/common/utils/featureValueValidation.ts
rg -n -F -e 'function toast' -e 'const toast =' -e 'export const toast' -e 'export function toast' frontend

Repository: Flagsmith/flagsmith

Length of output: 27604


Script executed:

nl -ba frontend/web/components/modals/create-feature/index.tsx | sed -n '374,452p;671,833p;897,952p;1115,1170p'
nl -ba frontend/web/project/toast.tsx | sed -n '1,240p'
rg -n -F -e 'onSaveFeatureValue' -e 'onSaveVariationValues' frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx
nl -ba frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx | sed -n '130,260p;450,650p'
nl -ba frontend/web/components/saveFeatureWithValidation.tsx 2>/dev/null || nl -ba frontend/web/components/saveFeatureWithValidation.ts
rg -n -F -e 'saveFeatureSegments' -e 'onSave' frontend/web/components/modals/create-feature/tabs/SegmentOverridesTab*
nl -ba frontend/web/components/modals/create-feature/components/IdentitySaveFooter.tsx

Repository: Flagsmith/flagsmith

Length of output: 40750


Script executed:

rg -n -F -e 'toast-messages' -e 'toast-message' frontend --glob '*.*'
rg --files frontend/web/components | rg 'FlagValueFooter|AddVariationButton|VariationOptions|SegmentOverridesTab'
rg -n -F -e 'onSaveFeatureValue' -e 'onSaveVariationValues' frontend/web/components/modals/create-feature/components frontend/web/components/modals/create-feature/tabs
rg -n -F -e 'max_variations' -e 'maximum variations' -e 'MAX_VARIATION' frontend/web/components/mv frontend/web/components/modals/create-feature frontend/common  || test "$?" -eq 1

Repository: Flagsmith/flagsmith

Length of output: 1808


Script executed:

nl -ba frontend/web/styles/components/_toast.scss | sed -n '1,180p'
nl -ba frontend/web/styles/project/_utils.scss | sed -n '1,80p'
nl -ba frontend/web/components/modals/FlagValueFooter.tsx | sed -n '1,260p'
nl -ba frontend/web/components/mv/AddVariationButton.tsx
nl -ba frontend/web/components/modals/create-feature/tabs/FeatureValueTab.tsx | sed -n '125,158p;390,435p;505,556p'
rg -n -F -e '<ToastMessages' -e 'ToastMessages' frontend/web

Repository: Flagsmith/flagsmith

Length of output: 19272


Script executed:

nl -ba frontend/web/main.js | sed -n '95,122p'
rg -n -F -e 'id="toast"' -e "id='toast'" -e "getElementById('toast')" -e 'getElementById("toast")' frontend/web frontend --glob '*.html' --glob '*.js' --glob '*.tsx' --glob '*.ts'

Repository: Flagsmith/flagsmith

Length of output: 1558


Summarise length errors in one toast.

If several values exceed the limit, one save creates a toast for each value. The stack can cover the editor for five seconds. Show one summary toast while preserving the individual error details.


const hasMultivariate =
props.environmentFlag?.multivariate_feature_state_values?.length

Expand Down Expand Up @@ -476,6 +504,11 @@ const CreateFeatureModal: FC<CreateFeatureModalProps> = (props) => {
!!projectFlag.multivariate_options &&
projectFlag.multivariate_options.length &&
controlValue < 0
const validateValueLengths =
(cb: (schedule?: boolean) => void) => (schedule?: boolean) => {
if (showValueLengthErrors()) return
cb(schedule)
}
const isVersionedChangeRequest = existingChangeRequest && isVersioned
const hideIdentityOverridesTab = Utils.getShouldHideIdentityOverridesTab()
const isDependenciesTabEnabled = !existingChangeRequest
Expand Down Expand Up @@ -577,7 +610,16 @@ const CreateFeatureModal: FC<CreateFeatureModalProps> = (props) => {
const hasVariationChanges =
variationChanges.values || variationChanges.added

const onSaveVariationValues = () =>
const onSaveVariationValues = () => {
const valueLengthErrors = getFeatureValueLengthErrors(
(projectFlag.multivariate_options || []).map(
(option: any, index: number) => ({
label: `Variant ${option.key || index + 1} value`,
value: option.string_value,
}),
),
)
if (showValueLengthErrors(valueLengthErrors)) return
openConfirm({
body: 'Variation values belong to the feature, so this applies to every environment straight away and is not part of a change request.',
noText: 'Cancel',
Expand Down Expand Up @@ -624,9 +666,11 @@ const CreateFeatureModal: FC<CreateFeatureModalProps> = (props) => {
title: 'Save variation values',
yesText: 'Save for all environments',
})
}

const saveFeatureValue = saveFeatureWithValidation(
(schedule?: boolean) => {
if (showValueLengthErrors()) return
if ((is4Eyes || schedule) && !identity) {
const approvable =
dependenciesChanged ||
Expand Down Expand Up @@ -831,7 +875,9 @@ const CreateFeatureModal: FC<CreateFeatureModalProps> = (props) => {
hasVariationChanges={hasVariationChanges}
onSaveVariationValues={onSaveVariationValues}
originalMultivariateOptions={originalMultivariateOptions}
onSaveFeatureValue={saveFeatureValue}
onSaveFeatureValue={validateValueLengths(
saveFeatureValue,
)}
onEnvironmentFlagChange={(changes: any) => {
setEnvironmentFlag((prev: any) => ({
...prev,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ const FeatureValueTab: FC<FeatureValueTabProps> = ({
<div className='form-group'>
<ValueEditor
label={valueTitle}
validateLength
labelAfter={
hasVariations && (
<ControlWeightChip percentage={controlPercentage} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const VariationValueInput: React.FC<VariationValueProps> = ({
: Constants.projectPermissions(ProjectPermission.CREATE_FEATURE),
<ValueEditor
label='Variation Value'
validateLength
labelTooltip={Constants.strings.REMOTE_CONFIG_DESCRIPTION_VARIATION}
className='full-width code-medium'
value={Utils.getTypedValue(Utils.featureStateToValue(value))}
Expand Down
Loading