mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-23 01:16:27 +02:00
feat: update UI to add hints about implicit ^ and $ (#4667)
This PR updates the UI to reflect the changes to the implicit ^ and $ that we now add. The changes are: 1. Show input adornments for ^ and $ when you create a pattern. 2. Mention that ^ and $ are added implicitly in description. 3. Checks the example you provide against the pattern with added ^ and $ + adds a test for that. Points 1 and 2:  ## Discussion point: I have not touched the information about the pattern yet as the PR that updates that is still in review (#4656), but it would be prudent to also update that info to make it clearer. I can address that in a follow-up PR.
This commit is contained in:
parent
03d6ed0c32
commit
f49cc8cd33
@ -37,7 +37,7 @@ export const FeatureNamingPatternInfo: React.FC<Props> = ({
|
|||||||
<dl id="feature-naming-pattern-info">
|
<dl id="feature-naming-pattern-info">
|
||||||
<dt>Pattern</dt>
|
<dt>Pattern</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<code>{featureNaming.pattern}</code>
|
<code>^{featureNaming.pattern}$</code>
|
||||||
</dd>
|
</dd>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(featureNaming?.example)}
|
condition={Boolean(featureNaming?.example)}
|
||||||
|
@ -4,7 +4,6 @@ import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
|||||||
|
|
||||||
export const FeatureFlagNamingTooltip: FC = () => {
|
export const FeatureFlagNamingTooltip: FC = () => {
|
||||||
const X = 'X';
|
const X = 'X';
|
||||||
const Y = 'Y';
|
|
||||||
const nx = 'n{X,}';
|
const nx = 'n{X,}';
|
||||||
const nxy = 'n{X,Y}';
|
const nxy = 'n{X,Y}';
|
||||||
return (
|
return (
|
||||||
@ -14,7 +13,7 @@ export const FeatureFlagNamingTooltip: FC = () => {
|
|||||||
<Box>
|
<Box>
|
||||||
<h3>Enforce a naming convention for feature flags</h3>
|
<h3>Enforce a naming convention for feature flags</h3>
|
||||||
<hr />
|
<hr />
|
||||||
<p>{`eg. ^[A-Za-z0-9]{2}[.][a-z]{4,12}$ matches 'a1.project'`}</p>
|
<p>{`eg. [A-Za-z0-9]{2}[.][a-z]{4,12} matches 'a1.project'`}</p>
|
||||||
<div className="scrollable">
|
<div className="scrollable">
|
||||||
<h3>Brackets:</h3>
|
<h3>Brackets:</h3>
|
||||||
<table>
|
<table>
|
||||||
|
@ -4,7 +4,7 @@ import { StickinessSelect } from 'component/feature/StrategyTypes/FlexibleStrate
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import Select from 'component/common/select';
|
import Select from 'component/common/select';
|
||||||
import { ProjectMode } from '../hooks/useProjectForm';
|
import { ProjectMode } from '../hooks/useProjectForm';
|
||||||
import { Box, styled, TextField } from '@mui/material';
|
import { Box, InputAdornment, styled, TextField } from '@mui/material';
|
||||||
import { CollaborationModeTooltip } from './CollaborationModeTooltip';
|
import { CollaborationModeTooltip } from './CollaborationModeTooltip';
|
||||||
import Input from 'component/common/Input/Input';
|
import Input from 'component/common/Input/Input';
|
||||||
import { FeatureTogglesLimitTooltip } from './FeatureTogglesLimitTooltip';
|
import { FeatureTogglesLimitTooltip } from './FeatureTogglesLimitTooltip';
|
||||||
@ -106,6 +106,10 @@ const StyledFlagNamingContainer = styled('div')(({ theme }) => ({
|
|||||||
'& > *': { width: '100%' },
|
'& > *': { width: '100%' },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const StyledPatternNamingExplanation = styled('div')(({ theme }) => ({
|
||||||
|
'p + p': { marginTop: theme.spacing(1) },
|
||||||
|
}));
|
||||||
|
|
||||||
export const validateFeatureNamingExample = ({
|
export const validateFeatureNamingExample = ({
|
||||||
pattern,
|
pattern,
|
||||||
example,
|
example,
|
||||||
@ -118,7 +122,7 @@ export const validateFeatureNamingExample = ({
|
|||||||
if (featureNamingPatternError || !example || !pattern) {
|
if (featureNamingPatternError || !example || !pattern) {
|
||||||
return { state: 'valid' };
|
return { state: 'valid' };
|
||||||
} else if (example && pattern) {
|
} else if (example && pattern) {
|
||||||
const regex = new RegExp(pattern);
|
const regex = new RegExp(`^${pattern}$`);
|
||||||
const matches = regex.test(example);
|
const matches = regex.test(example);
|
||||||
if (!matches) {
|
if (!matches) {
|
||||||
return { state: 'invalid', reason: 'Example does not match regex' };
|
return { state: 'invalid', reason: 'Example does not match regex' };
|
||||||
@ -354,8 +358,9 @@ const ProjectForm: React.FC<IProjectForm> = ({
|
|||||||
<FeatureFlagNamingTooltip />
|
<FeatureFlagNamingTooltip />
|
||||||
</Box>
|
</Box>
|
||||||
<StyledSubtitle>
|
<StyledSubtitle>
|
||||||
<p id="pattern-naming-description">
|
<StyledPatternNamingExplanation id="pattern-naming-description">
|
||||||
A feature flag naming pattern is a{' '}
|
<p>
|
||||||
|
Define a{' '}
|
||||||
<a
|
<a
|
||||||
href={`https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions`}
|
href={`https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -363,20 +368,35 @@ const ProjectForm: React.FC<IProjectForm> = ({
|
|||||||
>
|
>
|
||||||
JavaScript RegEx
|
JavaScript RegEx
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
used to enforce feature flag names within
|
used to enforce feature flag names
|
||||||
this project.
|
within this project. The regex will be
|
||||||
|
surrounded by a leading <code>^</code>{' '}
|
||||||
|
and a trailing <code>$</code>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Leave it empty if you don’t want to add a
|
Leave it empty if you don’t want to add
|
||||||
naming pattern.
|
a naming pattern.
|
||||||
</p>
|
</p>
|
||||||
|
</StyledPatternNamingExplanation>
|
||||||
</StyledSubtitle>
|
</StyledSubtitle>
|
||||||
<StyledFlagNamingContainer>
|
<StyledFlagNamingContainer>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
label={'Naming Pattern'}
|
label={'Naming Pattern'}
|
||||||
name="feature flag naming pattern"
|
name="feature flag naming pattern"
|
||||||
aria-describedby="pattern-naming-description"
|
aria-describedby="pattern-naming-description"
|
||||||
placeholder="^[A-Za-z]+\.[A-Za-z]+\.[A-Za-z0-9-]+$"
|
placeholder="[A-Za-z]+\.[A-Za-z]+\.[A-Za-z0-9-]+"
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
^
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
$
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
type={'text'}
|
type={'text'}
|
||||||
value={featureNamingPattern || ''}
|
value={featureNamingPattern || ''}
|
||||||
error={Boolean(errors.featureNamingPattern)}
|
error={Boolean(errors.featureNamingPattern)}
|
||||||
|
@ -47,4 +47,14 @@ describe('validateFeatureNaming', () => {
|
|||||||
expect(result.state).toBe(state);
|
expect(result.state).toBe(state);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test('the pattern gets an implicit leading ^ and trailing $ added', () => {
|
||||||
|
const result = validateFeatureNamingExample({
|
||||||
|
pattern: '[a-z]+',
|
||||||
|
example: 'not.valid',
|
||||||
|
featureNamingPatternError: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.state).toBe('invalid');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user