mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Move htmlIdGenerator to ui_framework (#13906)
* Move htmlIdGenerator to ui_framework * Use jest expect instead of chai * Add htmlIdGenerator to accessibility styleguide * Improve the writing of the accessibility styleguide
This commit is contained in:
parent
e46830e17e
commit
51aab7edb2
29 changed files with 87 additions and 33 deletions
|
@ -10,7 +10,7 @@ import createSelectHandler from '../lib/create_select_handler';
|
|||
import createTextHandler from '../lib/create_text_handler';
|
||||
import Vars from './vars';
|
||||
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
class CalculationAgg extends Component {
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import AggRow from './agg_row';
|
|||
import createChangeHandler from '../lib/create_change_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import createTextHandler from '../lib/create_text_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
export const DerivativeAgg = props => {
|
||||
const { siblings } = props;
|
||||
|
|
|
@ -6,7 +6,7 @@ import AggRow from './agg_row';
|
|||
import createChangeHandler from '../lib/create_change_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import createTextHandler from '../lib/create_text_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
export const FilterRatioAgg = props => {
|
||||
const {
|
||||
|
|
|
@ -8,7 +8,7 @@ import createChangeHandler from '../lib/create_change_handler';
|
|||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import createTextHandler from '../lib/create_text_handler';
|
||||
import createNumberHandler from '../lib/create_number_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
export const MovingAverageAgg = props => {
|
||||
const { siblings } = props;
|
||||
|
|
|
@ -10,7 +10,7 @@ import Select from 'react-select';
|
|||
import uuid from 'uuid';
|
||||
import createChangeHandler from '../lib/create_change_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
const newPercentile = (opts) => {
|
||||
return _.assign({ id: uuid.v1(), mode: 'line', shade: 0.2 }, opts);
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@ import AggRow from './agg_row';
|
|||
import createChangeHandler from '../lib/create_change_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import createTextHandler from '../lib/create_text_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
export const PercentileRankAgg = props => {
|
||||
const { series, panel, fields } = props;
|
||||
|
|
|
@ -6,7 +6,7 @@ import AggRow from './agg_row';
|
|||
import createChangeHandler from '../lib/create_change_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import createNumberHandler from '../lib/create_number_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
export const SerialDiffAgg = props => {
|
||||
const { siblings } = props;
|
||||
|
|
|
@ -5,7 +5,7 @@ import Select from 'react-select';
|
|||
import AggRow from './agg_row';
|
||||
import createChangeHandler from '../lib/create_change_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
function SeriesAgg(props) {
|
||||
const { model } = props;
|
||||
|
|
|
@ -5,7 +5,7 @@ import AggRow from './agg_row';
|
|||
import createChangeHandler from '../lib/create_change_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import createTextHandler from '../lib/create_text_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
export const Static = props => {
|
||||
const handleChange = createChangeHandler(props.onChange, props.model);
|
||||
|
|
|
@ -5,7 +5,7 @@ import FieldSelect from './field_select';
|
|||
import AggRow from './agg_row';
|
||||
import createChangeHandler from '../lib/create_change_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
function StandardAgg(props) {
|
||||
const { model, panel, series, fields } = props;
|
||||
|
|
|
@ -7,7 +7,7 @@ import Select from 'react-select';
|
|||
import createChangeHandler from '../lib/create_change_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import createTextHandler from '../lib/create_text_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
export const StandardDeviationAgg = props => {
|
||||
const { series, panel, fields } = props;
|
||||
|
|
|
@ -7,7 +7,7 @@ import Select from 'react-select';
|
|||
import createChangeHandler from '../lib/create_change_handler';
|
||||
import createSelectHandler from '../lib/create_select_handler';
|
||||
import createTextHandler from '../lib/create_text_handler';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
export const StandardSiblingAgg = props => {
|
||||
const { siblings } = props;
|
||||
|
|
|
@ -8,7 +8,7 @@ import FieldSelect from './aggs/field_select';
|
|||
import uuid from 'uuid';
|
||||
import IconSelect from './icon_select';
|
||||
import YesNo from './yes_no';
|
||||
import { htmlIdGenerator } from '../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
function newAnnotation() {
|
||||
return {
|
||||
|
|
|
@ -5,7 +5,7 @@ import AddDeleteButtons from './add_delete_buttons';
|
|||
import Select from 'react-select';
|
||||
import * as collectionActions from './lib/collection_actions';
|
||||
import ColorPicker from './color_picker';
|
||||
import { htmlIdGenerator } from '../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
class ColorRules extends Component {
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import FieldSelect from './aggs/field_select';
|
|||
import createSelectHandler from './lib/create_select_handler';
|
||||
import createTextHandler from './lib/create_text_handler';
|
||||
import YesNo from './yes_no';
|
||||
import { htmlIdGenerator } from '../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
export const IndexPattern = props => {
|
||||
const { fields, prefix } = props;
|
||||
|
|
|
@ -9,7 +9,7 @@ import ColorRules from '../color_rules';
|
|||
import ColorPicker from '../color_picker';
|
||||
import uuid from 'uuid';
|
||||
import YesNo from 'plugins/metrics/components/yes_no';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
class GaugePanelConfig extends Component {
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import ColorPicker from '../color_picker';
|
|||
import YesNo from '../yes_no';
|
||||
import MarkdownEditor from '../markdown_editor';
|
||||
import less from 'less/lib/less-browser';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
const lessC = less(window, { env: 'production' });
|
||||
|
||||
class MarkdownPanelConfig extends Component {
|
||||
|
|
|
@ -6,7 +6,7 @@ import createTextHandler from '../lib/create_text_handler';
|
|||
import ColorRules from '../color_rules';
|
||||
import YesNo from '../yes_no';
|
||||
import uuid from 'uuid';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
class MetricPanelConfig extends Component {
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import createSelectHandler from '../lib/create_select_handler';
|
|||
import createTextHandler from '../lib/create_text_handler';
|
||||
import ColorPicker from '../color_picker';
|
||||
import YesNo from '../yes_no';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
class TimeseriesPanelConfig extends Component {
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import ColorRules from '../color_rules';
|
|||
import ColorPicker from '../color_picker';
|
||||
import uuid from 'uuid';
|
||||
import YesNo from '../yes_no';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
class TopNPanelConfig extends Component {
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import createSelectHandler from './lib/create_select_handler';
|
|||
import createTextHandler from './lib/create_text_handler';
|
||||
import YesNo from './yes_no';
|
||||
import { IndexPattern } from './index_pattern';
|
||||
import { htmlIdGenerator } from '../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
export const SeriesConfig = props => {
|
||||
const defaults = { offset_time: '', value_template: '' };
|
||||
|
|
|
@ -6,7 +6,7 @@ import createSelectHandler from '../../lib/create_select_handler';
|
|||
import YesNo from '../../yes_no';
|
||||
import createTextHandler from '../../lib/create_text_handler';
|
||||
import { IndexPattern } from '../../index_pattern';
|
||||
import { htmlIdGenerator } from '../../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
function TimeseriesConfig(props) {
|
||||
const handleSelectChange = createSelectHandler(props.onChange);
|
||||
|
|
|
@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import React from 'react';
|
||||
import createLegendSeries from '../lib/create_legend_series';
|
||||
import reactcss from 'reactcss';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
function HorizontalLegend(props) {
|
||||
const rows = props.series.map(createLegendSeries(props));
|
||||
|
|
|
@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import React from 'react';
|
||||
import createLegendSeries from '../lib/create_legend_series';
|
||||
import reactcss from 'reactcss';
|
||||
import { htmlIdGenerator } from '../../lib/html_id_generator';
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
function VerticalLegend(props) {
|
||||
const rows = props.series.map(createLegendSeries(props));
|
||||
|
|
|
@ -50,6 +50,59 @@ You should always prefer the `<label for>` solution, since it also adds benefit
|
|||
for every user, by making the label clickable, to directly jump to the form
|
||||
element (or in case of checkboxes and radio buttons directly check them).
|
||||
|
||||
#### How to generate ids?
|
||||
|
||||
When labeling elements (and for some other accessibility tasks) you will often need
|
||||
ids. Ids must be unique within the page i.e. no duplicate ids in the rendered DOM
|
||||
at any time.
|
||||
|
||||
Since we have some components that are used multiple times on the page, you must
|
||||
make sure every instance of that component has a unique `id`. To make the generation
|
||||
of those `id`s easier, you can use the `htmlIdGenerator` service in the `ui_framework/services`.
|
||||
|
||||
A react component could use it as follows:
|
||||
|
||||
```jsx
|
||||
import { htmlIdGenerator } from 'ui_framework/services';
|
||||
|
||||
render() {
|
||||
// Create a new generator that will create ids deterministic
|
||||
const htmlId = htmlIdGenerator();
|
||||
return (<div>
|
||||
<label htmlFor={htmlId('agg')}>Aggregation</label>
|
||||
<input id={htmlId('agg')}/>
|
||||
</div>);
|
||||
}
|
||||
```
|
||||
|
||||
Each id generator you create by calling `htmlIdGenerator()` will generate unique but
|
||||
deterministic ids. As you can see in the above example, that single generator
|
||||
created the same id in the label's `htmlFor` as well as the input's `id`.
|
||||
|
||||
A single generator instance will create the same id when passed the same argument
|
||||
to the function multiple times. But two different generators will produce two different
|
||||
ids for the same argument to the function, as you can see in the following example:
|
||||
|
||||
```js
|
||||
const generatorOne = htmlIdGenerator();
|
||||
const generatorTwo = htmlIdGenerator();
|
||||
|
||||
// Those statements are always true:
|
||||
// Same generator
|
||||
generatorOne('foo') === generatorOne('foo');
|
||||
generatorOne('foo') !== generatorOne('bar');
|
||||
|
||||
// Different generator
|
||||
generatorOne('foo') !== generatorTwo('foo')
|
||||
```
|
||||
|
||||
This allows multiple instances of a single react component to now have different ids.
|
||||
If you include the above react component multiple times in the same page,
|
||||
each component instance will have a unique id, because each render method will use a different
|
||||
id generator.
|
||||
|
||||
You can use this service of course also outside of react.
|
||||
|
||||
### Don't use the `title` attribute
|
||||
|
||||
**TL;DR** *Don't use the `title` attribute, use tooltips, `aria-label`, etc. instead.*
|
||||
|
|
|
@ -1,40 +1,39 @@
|
|||
import { expect } from 'chai';
|
||||
import { htmlIdGenerator } from '../html_id_generator';
|
||||
import { htmlIdGenerator } from './html_id_generator';
|
||||
|
||||
describe('htmlIdGenerator', () => {
|
||||
|
||||
it('should return a function', () => {
|
||||
const fn = htmlIdGenerator();
|
||||
expect(fn).to.be.a('function');
|
||||
expect(typeof fn).toBe('function');
|
||||
});
|
||||
|
||||
it('should return an id ending with the specified suffix', () => {
|
||||
expect(htmlIdGenerator()('suf')).to.match(/suf$/);
|
||||
expect(htmlIdGenerator()('suf')).toMatch(/suf$/);
|
||||
});
|
||||
|
||||
it('should return an id beginning with the specified prefix', () => {
|
||||
expect(htmlIdGenerator('pref')('foo')).to.match(/^pref/);
|
||||
expect(htmlIdGenerator('pref')('foo')).toMatch(/^pref/);
|
||||
});
|
||||
|
||||
it('should create the same id for the same suffix', () => {
|
||||
const idGenerator = htmlIdGenerator();
|
||||
expect(idGenerator('foo')).to.equal(idGenerator('foo'));
|
||||
expect(idGenerator('foo')).toBe(idGenerator('foo'));
|
||||
});
|
||||
|
||||
it('should create different ids for different suffixes', () => {
|
||||
const idGenerator = htmlIdGenerator();
|
||||
expect(idGenerator('foo')).not.to.equal(idGenerator('bar'));
|
||||
expect(idGenerator('foo')).not.toBe(idGenerator('bar'));
|
||||
});
|
||||
|
||||
it('should generate different ids on different instances', () => {
|
||||
const idGenerator1 = htmlIdGenerator();
|
||||
const idGenerator2 = htmlIdGenerator();
|
||||
expect(idGenerator1('foo')).not.to.equal(idGenerator2('foo'));
|
||||
expect(idGenerator1('foo')).not.toBe(idGenerator2('foo'));
|
||||
});
|
||||
|
||||
it('should generate different ids if no suffix is passed', () => {
|
||||
const generator = htmlIdGenerator();
|
||||
expect(generator()).not.to.equal(generator());
|
||||
expect(generator()).not.toBe(generator());
|
||||
});
|
||||
|
||||
});
|
|
@ -1,2 +1,3 @@
|
|||
export { accessibleClickKeys } from './accessible_click_keys';
|
||||
export { comboBoxKeyCodes } from './combo_box_key_codes';
|
||||
export { htmlIdGenerator } from './html_id_generator';
|
||||
|
|
|
@ -5,6 +5,7 @@ export { keyCodes };
|
|||
export {
|
||||
accessibleClickKeys,
|
||||
comboBoxKeyCodes,
|
||||
htmlIdGenerator
|
||||
} from './accessibility';
|
||||
|
||||
export { SortableProperties } from './sort';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue