diff --git a/CHANGELOG.md b/CHANGELOG.md index bba4ed42698..768263c60ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## [`master`](https://github.com/elastic/eui/tree/master) -No public interface changes since `22.2.0`. +- Improved `htmlIdGenerator` when supplying both `prefix` and `suffix` ([#3076](https://github.com/elastic/eui/pull/3076)) ## [`22.2.0`](https://github.com/elastic/eui/tree/v22.2.0) diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index 377a0806a94..9e9b0ee66cb 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -122,6 +122,8 @@ import { HighlightAndMarkExample } from './views/highlight_and_mark/highlight_an import { HorizontalRuleExample } from './views/horizontal_rule/horizontal_rule_example'; +import { HtmlIdGeneratorExample } from './views/html_id_generator/html_id_generator_example'; + import { I18nExample } from './views/i18n/i18n_example'; import { IconExample } from './views/icon/icon_example'; @@ -414,6 +416,7 @@ const navigation = [ ErrorBoundaryExample, FocusTrapExample, HighlightAndMarkExample, + HtmlIdGeneratorExample, InnerTextExample, I18nExample, IsColorDarkExample, diff --git a/src-docs/src/views/html_id_generator/bothPrefixSuffix.js b/src-docs/src/views/html_id_generator/bothPrefixSuffix.js new file mode 100644 index 00000000000..7c20d4a5e34 --- /dev/null +++ b/src-docs/src/views/html_id_generator/bothPrefixSuffix.js @@ -0,0 +1,76 @@ +import React, { Component, Fragment } from 'react'; + +import { + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiCode, + EuiFormRow, +} from '../../../../src/components'; +import { htmlIdGenerator } from '../../../../src/services'; + +export class PrefixSufix extends Component { + constructor(props) { + super(props); + + this.state = { + prefix: 'Some', + suffix: 'Id', + id1: htmlIdGenerator('Some')('Id'), + }; + } + + onPrefixChange = e => { + const prefix = e.target.value; + const { suffix } = this.state; + + this.setState({ + prefix, + id1: htmlIdGenerator(prefix)(suffix), + }); + }; + + onSuffixChange = e => { + const suffix = e.target.value; + const { prefix } = this.state; + + this.setState({ + suffix, + id1: htmlIdGenerator(prefix)(suffix), + }); + }; + + render() { + const { prefix, suffix, id1 } = this.state; + return ( + + + + + + + + + + + + + + + {id1} + + ); + } +} diff --git a/src-docs/src/views/html_id_generator/htmlIdGenerator.js b/src-docs/src/views/html_id_generator/htmlIdGenerator.js new file mode 100644 index 00000000000..d534e049226 --- /dev/null +++ b/src-docs/src/views/html_id_generator/htmlIdGenerator.js @@ -0,0 +1,43 @@ +import React, { Component, Fragment } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiCode, +} from '../../../../src/components'; + +import { htmlIdGenerator } from '../../../../src/services'; + +export default class extends Component { + constructor(props) { + super(props); + + this.state = { + value: htmlIdGenerator()(), + }; + } + + reGenerate = () => { + this.setState({ value: htmlIdGenerator()() }); + }; + + render() { + const { value } = this.state; + return ( + + + + {value} + + + Regenerate + + + + ); + } +} diff --git a/src-docs/src/views/html_id_generator/htmlIdGeneratorPrefix.js b/src-docs/src/views/html_id_generator/htmlIdGeneratorPrefix.js new file mode 100644 index 00000000000..2bd726415d2 --- /dev/null +++ b/src-docs/src/views/html_id_generator/htmlIdGeneratorPrefix.js @@ -0,0 +1,54 @@ +import React, { Component, Fragment } from 'react'; + +import { + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiCode, + EuiFormRow, +} from '../../../../src/components'; +import { htmlIdGenerator } from '../../../../src/services'; + +export class HtmlIdGeneratorPrefix extends Component { + constructor(props) { + super(props); + + this.state = { + prefix: 'Id', + id1: htmlIdGenerator('Id')(), + }; + } + + onSearchChange = e => { + const prefix = e.target.value; + this.setState({ + prefix, + id1: htmlIdGenerator(prefix)(), + }); + }; + + render() { + const { prefix, id1 } = this.state; + return ( + + + + + + + + + + {id1} + + ); + } +} diff --git a/src-docs/src/views/html_id_generator/htmlIdGeneratorSuffix.js b/src-docs/src/views/html_id_generator/htmlIdGeneratorSuffix.js new file mode 100644 index 00000000000..3f837874ff3 --- /dev/null +++ b/src-docs/src/views/html_id_generator/htmlIdGeneratorSuffix.js @@ -0,0 +1,54 @@ +import React, { Component, Fragment } from 'react'; + +import { + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiCode, + EuiFormRow, +} from '../../../../src/components'; +import { htmlIdGenerator } from '../../../../src/services'; + +export class HtmlIdGeneratorSuffix extends Component { + constructor(props) { + super(props); + + this.state = { + suffix: 'Id', + id1: htmlIdGenerator()('Id'), + }; + } + + onSuffixChange = e => { + const suffix = e.target.value; + this.setState({ + suffix, + id1: htmlIdGenerator()(suffix), + }); + }; + + render() { + const { suffix, id1 } = this.state; + return ( + + + + + + + + + + {id1} + + ); + } +} diff --git a/src-docs/src/views/html_id_generator/html_id_generator_example.js b/src-docs/src/views/html_id_generator/html_id_generator_example.js new file mode 100644 index 00000000000..9affbf5a201 --- /dev/null +++ b/src-docs/src/views/html_id_generator/html_id_generator_example.js @@ -0,0 +1,120 @@ +import React from 'react'; + +import { renderToHtml } from '../../services'; + +import { GuideSectionTypes } from '../../components'; +import { EuiCode } from '../../../../src/components'; + +import IdGenerator from './htmlIdGenerator'; +import { HtmlIdGeneratorPrefix } from './htmlIdGeneratorPrefix'; +import { HtmlIdGeneratorSuffix } from './htmlIdGeneratorSuffix'; +import { PrefixSufix } from './bothPrefixSuffix'; + +const htmlIdGeneratorSource = require('!!raw-loader!./htmlIdGenerator'); +const htmlIdGeneratorHtml = renderToHtml(IdGenerator); +const htmlIdGeneratorSnippet = ' htmlIdGenerator()()'; + +const htmlIdGeneratorPrefixSource = require('!!raw-loader!./htmlIdGeneratorPrefix'); +const htmlIdGeneratorPrefixHtml = renderToHtml(HtmlIdGeneratorPrefix); +const htmlIdGeneratorPrefixSnippet = " htmlIdGenerator('prefix')()"; + +const HtmlIdGeneratorSuffixSource = require('!!raw-loader!./htmlIdGeneratorSuffix'); +const HtmlIdGeneratorSuffixHtml = renderToHtml(HtmlIdGeneratorSuffix); +const suffixSnippet = " htmlIdGenerator()('suffix')"; + +const PrefixSufixSource = require('!!raw-loader!./bothPrefixSuffix'); +const PrefixSufixHtml = renderToHtml(PrefixSufix); +const prefixSuffixSnippet = " htmlIdGenerator('prefix')('suffix')"; + +export const HtmlIdGeneratorExample = { + title: 'Html Id Generator', + sections: [ + { + source: [ + { + type: GuideSectionTypes.JS, + code: htmlIdGeneratorSource, + }, + { + type: GuideSectionTypes.HTML, + code: htmlIdGeneratorHtml, + }, + ], + text: ( +

+ Use htmlIdGenerator to generate unique IDs for + elements with an optional prefix and/or{' '} + suffix. The first call to{' '} + htmlIdGenerator accepts the prefix as an optional + argument and returns a second function which accepts an optional + suffix and returns the generated ID. +

+ ), + snippet: htmlIdGeneratorSnippet, + demo: , + }, + { + title: 'ID generator with prefix', + source: [ + { + type: GuideSectionTypes.JS, + code: htmlIdGeneratorPrefixSource, + }, + { + type: GuideSectionTypes.HTML, + code: htmlIdGeneratorPrefixHtml, + }, + ], + text: ( +

+ Provide a prefix to the generator to get an ID that + starts with the specified prefix. +

+ ), + snippet: htmlIdGeneratorPrefixSnippet, + demo: , + }, + { + title: 'ID generator with suffix', + source: [ + { + type: GuideSectionTypes.JS, + code: HtmlIdGeneratorSuffixSource, + }, + { + type: GuideSectionTypes.HTML, + code: HtmlIdGeneratorSuffixHtml, + }, + ], + text: ( +

+ Provide a suffix to the generator to get an ID that + starts with the specified suffix. +

+ ), + snippet: suffixSnippet, + demo: , + }, + { + title: 'ID generator with both prefix and suffix', + source: [ + { + type: GuideSectionTypes.JS, + code: PrefixSufixSource, + }, + { + type: GuideSectionTypes.HTML, + code: PrefixSufixHtml, + }, + ], + text: ( +

+ The HtmlIdGenerator is capable of generating an ID + with both a specified prefix and suffix. +

+ ), + snippet: prefixSuffixSnippet, + demo: , + }, + ], +}; diff --git a/src/services/accessibility/html_id_generator.ts b/src/services/accessibility/html_id_generator.ts index 21a8d80ac3b..2ff468933b5 100644 --- a/src/services/accessibility/html_id_generator.ts +++ b/src/services/accessibility/html_id_generator.ts @@ -7,7 +7,11 @@ import uuid from 'uuid'; * specify it, it generates a random id prefix. If you specify a custom prefix * it should begin with an letter to be HTML4 compliant. */ -export function htmlIdGenerator(idPrefix?: string) { - const prefix = idPrefix || `i${uuid.v1()}`; - return (suffix?: string) => `${prefix}_${suffix || uuid.v1()}`; +export function htmlIdGenerator(idPrefix: string = '') { + const staticUuid = uuid.v1(); + return (idSuffix: string = '') => { + const prefix = `${idPrefix}${idPrefix !== '' ? '_' : 'i'}`; + const suffix = idSuffix ? `_${idSuffix}` : ''; + return `${prefix}${suffix ? staticUuid : uuid.v1()}${suffix}`; + }; }