Skip to content

Commit 0cd6188

Browse files
zachraymerZach Raymer
andauthored
fix(cc-widgets): update station login component to use international dialing prop (#654)
Co-authored-by: Zach Raymer <zraymer@cisco.com>
1 parent 936e59c commit 0cd6188

8 files changed

Lines changed: 163 additions & 41 deletions

File tree

packages/contact-center/cc-components/src/components/StationLogin/station-login.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
saveConfirmCancelClicked,
1717
updateDialNumberLabel,
1818
handleCCSignoutKeyDown,
19+
INTERNATIONAL_DIAL_NUMBER_REGEX,
1920
} from './station-login.utils';
2021
import {withMetrics} from '@webex/cc-ui-logging';
2122

@@ -49,8 +50,15 @@ const StationLoginComponent: React.FunctionComponent<StationLoginComponentProps>
4950
dialNumberValue,
5051
setSelectedTeamId,
5152
hideDesktopLogin,
53+
allowInternationalDn,
5254
} = props;
5355

56+
// Determine the regex to use for dial number validation:
57+
// 1. If allowInternationalDn is true, use international regex
58+
// 2. Otherwise, use dialNumberRegex from agentConfig (if provided)
59+
// 3. If dialNumberRegex is null/undefined, validateDialNumber will fall back to US regex
60+
const resolvedDialNumberRegex = allowInternationalDn ? INTERNATIONAL_DIAL_NUMBER_REGEX : dialNumberRegex;
61+
5462
const [dialNumberLabel, setDialNumberLabel] = useState<string>('');
5563

5664
const [dialNumberPlaceholder, setDialNumberPlaceholder] = useState<string>('');
@@ -262,7 +270,7 @@ const StationLoginComponent: React.FunctionComponent<StationLoginComponentProps>
262270
setDialNumber,
263271
setShowDNError,
264272
setDNErrorText,
265-
dialNumberRegex,
273+
resolvedDialNumberRegex,
266274
setCurrentLoginOptions,
267275
selectedDeviceType,
268276
logger

packages/contact-center/cc-components/src/components/StationLogin/station-login.types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,13 @@ export interface IStationLoginProps {
209209
* Default: undefined (shows Desktop mode)
210210
*/
211211
hideDesktopLogin?: boolean;
212+
213+
/**
214+
* If true, uses international dial number regex for validation.
215+
* If false or undefined, uses dialNumberRegex from agentConfig or falls back to US regex.
216+
* Default: undefined (uses dialNumberRegex or US fallback)
217+
*/
218+
allowInternationalDn?: boolean;
212219
}
213220

214221
export interface LoginOptionsState {
@@ -251,4 +258,5 @@ export type StationLoginComponentProps = Pick<
251258
| 'setSelectedTeamId'
252259
| 'selectedTeamId'
253260
| 'hideDesktopLogin'
261+
| 'allowInternationalDn'
254262
>;

packages/contact-center/cc-components/src/components/StationLogin/station-login.utils.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,28 @@ const updateDialNumberLabel = (
104104
}
105105
};
106106

107+
// Default US dial number regex fallback
108+
const DEFAULT_US_DIAL_NUMBER_REGEX = '1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}';
109+
// International dial number regex
110+
const INTERNATIONAL_DIAL_NUMBER_REGEX = '^\\+?[0-9]{7,15}([,]{1,10}[0-9]+)?$';
111+
107112
/**
108113
* Runs validation tests on a string given as a Dial Number
109-
* @param {string} input
114+
* @param {string} input - The dial number to validate
115+
* @param {string | null} dialNumberRegex - Optional regex pattern for validation. If null, falls back to default US regex.
116+
* @param {function} setDNErrorText - Callback to set error text
117+
* @param {object} logger - Logger instance
110118
* @returns {boolean} whether or not to show a validation error
111119
*/
112-
const validateDialNumber = (input: string, setDNErrorText: (error: string) => void, logger): boolean => {
120+
const validateDialNumber = (
121+
input: string,
122+
dialNumberRegex: string | null,
123+
setDNErrorText: (error: string) => void,
124+
logger
125+
): boolean => {
113126
try {
114-
const regexForDn = new RegExp('^\\+?[0-9]{7,15}([,]{1,10}[0-9]+)?$');
127+
const regexPattern = dialNumberRegex ?? DEFAULT_US_DIAL_NUMBER_REGEX;
128+
const regexForDn = new RegExp(regexPattern);
115129
if (regexForDn.test(input)) {
116130
return false;
117131
}
@@ -266,7 +280,7 @@ const handleDNInputChanged = (
266280
setDNErrorText(`${LoginOptions[selectedDeviceType]} ${StationLoginLabels.IS_REQUIRED}`);
267281
setShowDNError(true);
268282
} else if (selectedDeviceType === DIAL_NUMBER) {
269-
setShowDNError(validateDialNumber(input, setDNErrorText, logger));
283+
setShowDNError(validateDialNumber(input, dialNumberRegex, setDNErrorText, logger));
270284
} else {
271285
setShowDNError(false);
272286
}
@@ -355,4 +369,6 @@ export {
355369
handleTeamSelectChanged,
356370
handleOnCCSignOut,
357371
handleCCSignoutKeyDown,
372+
DEFAULT_US_DIAL_NUMBER_REGEX,
373+
INTERNATIONAL_DIAL_NUMBER_REGEX,
358374
};

packages/contact-center/cc-components/tests/components/StationLogin/station-login.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,7 @@ describe('Station Login Component', () => {
906906
}) as unknown as typeof RegExp;
907907

908908
const mockSetError = jest.fn();
909-
const result = stationLoginUtils.validateDialNumber('invalid', mockSetError, loggerMock);
909+
const result = stationLoginUtils.validateDialNumber('invalid', null, mockSetError, loggerMock);
910910

911911
expect(loggerMock.error).toHaveBeenCalledWith('CC-Widgets: StationLogin: Error in validateDialNumber', {
912912
module: 'cc-components#station-login.utils.tsx',

packages/contact-center/cc-components/tests/components/StationLogin/station-login.utils.tsx

Lines changed: 80 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
handleTeamSelectChanged,
1616
handleOnCCSignOut,
1717
handleCCSignoutKeyDown,
18+
DEFAULT_US_DIAL_NUMBER_REGEX,
19+
INTERNATIONAL_DIAL_NUMBER_REGEX,
1820
} from '../../../src/components/StationLogin/station-login.utils';
1921
import {StationLoginLabels} from '../../../src/components/StationLogin/constants';
2022

@@ -224,46 +226,90 @@ describe('Station Login Utils', () => {
224226
mockSetDNErrorText.mockClear();
225227
});
226228

227-
it('should return false for valid US dial number with default regex', () => {
228-
const validNumber = '15552234567';
229-
const result = validateDialNumber(validNumber, mockSetDNErrorText, loggerMock);
230-
expect(result).toBe(false);
231-
expect(mockSetDNErrorText).not.toHaveBeenCalled();
232-
});
229+
describe('with international regex', () => {
230+
it('should return false for valid international dial number with + prefix', () => {
231+
const validNumber = '+442071234567'; // UK number
232+
const result = validateDialNumber(validNumber, INTERNATIONAL_DIAL_NUMBER_REGEX, mockSetDNErrorText, loggerMock);
233+
expect(result).toBe(false);
234+
expect(mockSetDNErrorText).not.toHaveBeenCalled();
235+
});
233236

234-
it('should return false for valid international dial number with + prefix', () => {
235-
const validNumber = '+442071234567'; // UK number
236-
const result = validateDialNumber(validNumber, mockSetDNErrorText, loggerMock);
237-
expect(result).toBe(false);
238-
expect(mockSetDNErrorText).not.toHaveBeenCalled();
239-
});
237+
it('should return false for valid international dial number without + prefix', () => {
238+
const validNumber = '442071234567'; // UK number without +
239+
const result = validateDialNumber(validNumber, INTERNATIONAL_DIAL_NUMBER_REGEX, mockSetDNErrorText, loggerMock);
240+
expect(result).toBe(false);
241+
expect(mockSetDNErrorText).not.toHaveBeenCalled();
242+
});
240243

241-
it('should return false for valid international dial number without + prefix', () => {
242-
const validNumber = '442071234567'; // UK number without +
243-
const result = validateDialNumber(validNumber, mockSetDNErrorText, loggerMock);
244-
expect(result).toBe(false);
245-
expect(mockSetDNErrorText).not.toHaveBeenCalled();
246-
});
244+
it('should return false for valid short international dial number (7 digits)', () => {
245+
const validNumber = '1234567'; // 7 digit minimum
246+
const result = validateDialNumber(validNumber, INTERNATIONAL_DIAL_NUMBER_REGEX, mockSetDNErrorText, loggerMock);
247+
expect(result).toBe(false);
248+
expect(mockSetDNErrorText).not.toHaveBeenCalled();
249+
});
250+
251+
it('should return true for invalid dial number (too short) and set error text', () => {
252+
const invalidNumber = '911'; // Too short (less than 7 digits)
253+
const result = validateDialNumber(
254+
invalidNumber,
255+
INTERNATIONAL_DIAL_NUMBER_REGEX,
256+
mockSetDNErrorText,
257+
loggerMock
258+
);
259+
expect(result).toBe(true);
260+
expect(mockSetDNErrorText).toHaveBeenCalledWith(StationLoginLabels.DN_FORMAT_ERROR);
261+
});
247262

248-
it('should return false for valid short international dial number', () => {
249-
const validNumber = '1234567'; // 7 digit minimum
250-
const result = validateDialNumber(validNumber, mockSetDNErrorText, loggerMock);
251-
expect(result).toBe(false);
252-
expect(mockSetDNErrorText).not.toHaveBeenCalled();
263+
it('should return true for invalid dial number (too long) and set error text', () => {
264+
const invalidNumber = '1234567890123456'; // Too long (more than 15 digits)
265+
const result = validateDialNumber(
266+
invalidNumber,
267+
INTERNATIONAL_DIAL_NUMBER_REGEX,
268+
mockSetDNErrorText,
269+
loggerMock
270+
);
271+
expect(result).toBe(true);
272+
expect(mockSetDNErrorText).toHaveBeenCalledWith(StationLoginLabels.DN_FORMAT_ERROR);
273+
});
253274
});
254275

255-
it('should return true for invalid dial number (too short) and set error text', () => {
256-
const invalidNumber = '911'; // Too short (less than 7 digits)
257-
const result = validateDialNumber(invalidNumber, mockSetDNErrorText, loggerMock);
258-
expect(result).toBe(true);
259-
expect(mockSetDNErrorText).toHaveBeenCalledWith(StationLoginLabels.DN_FORMAT_ERROR);
276+
describe('with US regex (null fallback)', () => {
277+
it('should return false for valid US dial number with null regex (uses default US)', () => {
278+
const validNumber = '15552234567';
279+
const result = validateDialNumber(validNumber, null, mockSetDNErrorText, loggerMock);
280+
expect(result).toBe(false);
281+
expect(mockSetDNErrorText).not.toHaveBeenCalled();
282+
});
283+
284+
it('should return true for international number when using US regex fallback', () => {
285+
const internationalNumber = '+442071234567'; // UK number - invalid for US regex
286+
const result = validateDialNumber(internationalNumber, null, mockSetDNErrorText, loggerMock);
287+
expect(result).toBe(true);
288+
expect(mockSetDNErrorText).toHaveBeenCalledWith(StationLoginLabels.DN_FORMAT_ERROR);
289+
});
290+
291+
it('should return true for short number when using US regex fallback', () => {
292+
const shortNumber = '1234567'; // Too short for US format
293+
const result = validateDialNumber(shortNumber, null, mockSetDNErrorText, loggerMock);
294+
expect(result).toBe(true);
295+
expect(mockSetDNErrorText).toHaveBeenCalledWith(StationLoginLabels.DN_FORMAT_ERROR);
296+
});
260297
});
261298

262-
it('should return true for invalid dial number (too long) and set error text', () => {
263-
const invalidNumber = '1234567890123456'; // Too long (more than 15 digits)
264-
const result = validateDialNumber(invalidNumber, mockSetDNErrorText, loggerMock);
265-
expect(result).toBe(true);
266-
expect(mockSetDNErrorText).toHaveBeenCalledWith(StationLoginLabels.DN_FORMAT_ERROR);
299+
describe('with custom regex from agentConfig', () => {
300+
it('should return false for valid number matching custom regex', () => {
301+
const validNumber = '15552234567';
302+
const result = validateDialNumber(validNumber, DEFAULT_US_DIAL_NUMBER_REGEX, mockSetDNErrorText, loggerMock);
303+
expect(result).toBe(false);
304+
expect(mockSetDNErrorText).not.toHaveBeenCalled();
305+
});
306+
307+
it('should return true for invalid number not matching custom regex', () => {
308+
const invalidNumber = '911';
309+
const result = validateDialNumber(invalidNumber, DEFAULT_US_DIAL_NUMBER_REGEX, mockSetDNErrorText, loggerMock);
310+
expect(result).toBe(true);
311+
expect(mockSetDNErrorText).toHaveBeenCalledWith(StationLoginLabels.DN_FORMAT_ERROR);
312+
});
267313
});
268314
});
269315

@@ -835,7 +881,7 @@ describe('Station Login Utils', () => {
835881
}) as unknown as typeof RegExp;
836882

837883
const mockSetError = jest.fn();
838-
const result = validateDialNumber('15551234567', mockSetError, loggerMock);
884+
const result = validateDialNumber('15551234567', null, mockSetError, loggerMock);
839885

840886
expect(loggerMock.error).toHaveBeenCalledWith('CC-Widgets: StationLogin: Error in validateDialNumber', {
841887
module: 'cc-components#station-login.utils.tsx',

packages/contact-center/station-login/src/station-login/index.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,17 @@ import {useStationLogin} from '../helper';
88
import {StationLoginProps} from './station-login.types';
99

1010
const StationLoginInternal: React.FunctionComponent<StationLoginProps> = observer(
11-
({onLogin, onLogout, onCCSignOut, profileMode, onSaveStart, onSaveEnd, doStationLogout, hideDesktopLogin}) => {
11+
({
12+
onLogin,
13+
onLogout,
14+
onCCSignOut,
15+
profileMode,
16+
onSaveStart,
17+
onSaveEnd,
18+
doStationLogout,
19+
hideDesktopLogin,
20+
allowInternationalDn,
21+
}) => {
1222
const {
1323
cc,
1424
teams,
@@ -54,6 +64,7 @@ const StationLoginInternal: React.FunctionComponent<StationLoginProps> = observe
5464
logger,
5565
profileMode,
5666
hideDesktopLogin,
67+
allowInternationalDn,
5768
};
5869

5970
return <StationLoginComponent {...props} />;

packages/contact-center/station-login/src/station-login/station-login.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ export type StationLoginProps = Pick<IStationLoginProps, 'profileMode'> &
2828
| 'teamId'
2929
| 'doStationLogout'
3030
| 'hideDesktopLogin'
31+
| 'allowInternationalDn'
3132
>
3233
>;

widgets-samples/cc/samples-cc-react-app/src/App.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ function App() {
8484
const savedHideDesktopLogin = window.localStorage.getItem('hideDesktopLogin');
8585
return savedHideDesktopLogin === 'true';
8686
});
87+
const [allowInternationalDn, setAllowInternationalDn] = useState(() => {
88+
const savedAllowInternationalDn = window.localStorage.getItem('allowInternationalDn');
89+
return savedAllowInternationalDn === 'true';
90+
});
8791

8892
const handleSaveStart = () => {
8993
setShowLoader(true);
@@ -767,6 +771,19 @@ function App() {
767771
setDoStationLogout(!doStationLogout);
768772
}}
769773
/>
774+
<Checkbox
775+
data-testid="samples:allow-international-dn-checkbox"
776+
checked={allowInternationalDn}
777+
aria-label="allow international dial numbers checkbox"
778+
id="allow-international-dn-checkbox"
779+
label="Allow International Dial Numbers"
780+
// @ts-expect-error: TODO: https://github.com/momentum-design/momentum-design/pull/1118
781+
onchange={() => {
782+
const newValue = !allowInternationalDn;
783+
setAllowInternationalDn(newValue);
784+
window.localStorage.setItem('allowInternationalDn', String(newValue));
785+
}}
786+
/>
770787
</div>
771788
<div className="station-login">
772789
<StationLogin
@@ -776,6 +793,7 @@ function App() {
776793
profileMode={false}
777794
doStationLogout={doStationLogout}
778795
hideDesktopLogin={hideDesktopLogin}
796+
allowInternationalDn={allowInternationalDn}
779797
/>
780798
</div>
781799
</fieldset>
@@ -799,13 +817,27 @@ function App() {
799817
setHideDesktopLogin(!hideDesktopLogin);
800818
}}
801819
/>
820+
<Checkbox
821+
data-testid="samples:allow-international-dn-profile-checkbox"
822+
checked={allowInternationalDn}
823+
aria-label="allow international dial numbers checkbox"
824+
id="allow-international-dn-profile-checkbox"
825+
label="Allow International Dial Numbers"
826+
// @ts-expect-error: TODO: https://github.com/momentum-design/momentum-design/pull/1118
827+
onchange={() => {
828+
const newValue = !allowInternationalDn;
829+
setAllowInternationalDn(newValue);
830+
window.localStorage.setItem('allowInternationalDn', String(newValue));
831+
}}
832+
/>
802833
</div>
803834
<div className="station-login">
804835
<StationLogin
805836
profileMode={true}
806837
onSaveStart={handleSaveStart}
807838
onSaveEnd={handleSaveEnd}
808839
hideDesktopLogin={hideDesktopLogin}
840+
allowInternationalDn={allowInternationalDn}
809841
/>
810842
</div>
811843
</fieldset>

0 commit comments

Comments
 (0)