/** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: MPL-2.0 */ /** * @module RegexValidator * RegexValidator components are used to provide input forms for regex values, along with a toggle-able validation input which does not get saved to the model. * * @example * ```js * const attrExample = { * name: 'valName', * options: { * helpText: 'Shows in tooltip', * subText: 'Shows underneath label', * docLink: 'Adds docs link to subText if present', * defaultValue: 'hello', // Shows if no value on model * } * } * * ``` * @param {string} value - the value of the main input which will be updated in onChange * @param {func} [onChange] - the action that should trigger when pattern input is changed. Required when attr is provided. * @param {string} [labelString] - Form label. Anticipated from form-field. Required when attr is provided. * @param {object} [attr] - attribute from model. Anticipated from form-field. Example of attribute shape above. When not provided pattern input is hidden * @param {string} [testInputLabel] - label for test input * @param {string} [testInputSubText] - sub text for test input * @param {boolean} [showGroups] - show groupings based on pattern and test input * @param {func} [onValidate] - action triggered every time the test string is validated against the regex -- passes testValue and captureGroups */ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; export default class RegexValidator extends Component { @tracked testValue = ''; @tracked showTestValue = false; constructor() { super(...arguments); this.showTestValue = !this.args.attr; } get testInputLabel() { return this.args.testInputLabel || 'Test string'; } get regex() { return new RegExp(this.args.value, 'g'); } get regexError() { const testString = this.testValue; if (!testString || !this.args.value) return false; const matchArray = testString.toString().match(this.regex); if (this.args.onValidate) { this.args.onValidate(this.testValue, this.captureGroups); } return testString !== matchArray?.join(''); } get captureGroups() { const result = this.regex.exec(this.testValue); if (result) { // first item is full string match but we are only interested in the captured groups const [fullMatch, ...matches] = result; // eslint-disable-line const groups = matches.map((m, index) => ({ position: `$${index + 1}`, value: m })); // push named capture groups into array -> eg (\d{4}) if (result.groups) { for (const key in result.groups) { groups.push({ position: `$${key}`, value: result.groups[key] }); } } return groups; } return []; } @action updateTestValue(evt) { this.testValue = evt.target.value; } @action toggleTestValue() { this.showTestValue = !this.showTestValue; } }