Skip to content

Commit fb71cd1

Browse files
authored
Fix ID generation to ensure it produces valid CSS identifiers (#3139)
* Move the logic of generating the ID into its own utility function. * Ensure the utility function generates IDs that are valid CSS identifiers. `shortid.generate()` by itself will generates IDs that are not always valid¹ CSS identifiers (e.g. will start with a digit). So, when other parts of the codebase will use those IDs as a selector, a `SyntaxError`² will be thrown (i.e. `'#...' is not a valid selector`). The `slds-` prefix is used so to keep things simple. `shortid.characters()` requires "a string of all 64 unique characters"³, which means that in order to generate valid CSS identifiers a-z, A-Z, and some "ISO 10646 characters U+00A0 and higher"¹ will need to be included. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ¹ From https://www.w3.org/TR/CSS21/syndata.html#value-def-identifier " In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. " ² https://dom.spec.whatwg.org/#selectors ³ https://github.com/dylang/shortid/blob/937ce2c8dd7001baec1785cb8dce6e6fe1bcf61f/README.md#shortidcharactersstring
1 parent b2ab369 commit fb71cd1

File tree

51 files changed

+157
-234
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+157
-234
lines changed

components/accordion/index.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
import React, { Component } from 'react';
99
import PropTypes from 'prop-types';
1010
import classNames from 'classnames';
11-
import shortid from 'shortid';
1211

1312
import { ACCORDION } from '../../utilities/constants';
13+
import generateId from '../../utilities/generate-id';
1414

1515
const propTypes = {
1616
/**
@@ -49,7 +49,7 @@ class Accordion extends Component {
4949
super(props);
5050
this.state = { currButtonIndex: 0 };
5151
this.summaryButtons = [];
52-
this.generatedId = shortid.generate();
52+
this.generatedId = generateId();
5353
}
5454

5555
componentDidUpdate(prevProps, prevState) {

components/app-launcher/index.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import PropTypes from 'prop-types';
1010
import classNames from 'classnames';
1111

1212
import isFunction from 'lodash.isfunction';
13-
import shortid from 'shortid';
1413

1514
// This component's `checkProps` which issues warnings to developers about properties when in development mode (similar to React's built in development tools)
1615
import checkProps from './check-props';
@@ -20,6 +19,7 @@ import componentDoc from './component.json';
2019
import Modal from '../modal';
2120

2221
import { APP_LAUNCHER } from '../../utilities/constants';
22+
import generateId from '../../utilities/generate-id';
2323

2424
const defaultProps = {
2525
assistiveText: {
@@ -127,7 +127,7 @@ class AppLauncher extends React.Component {
127127

128128
constructor(props) {
129129
super(props);
130-
this.generatedId = shortid.generate();
130+
this.generatedId = generateId();
131131
this.state = {
132132
isOpen: false,
133133
};

components/badge/index.jsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,18 @@ import React from 'react';
1010
import PropTypes from 'prop-types';
1111
import classNames from 'classnames';
1212

13-
// ### shortid
14-
// [npmjs.com/package/shortid](https://www.npmjs.com/package/shortid)
15-
// shortid is a short, non-sequential, url-friendly, unique id generator
16-
import shortid from 'shortid';
17-
1813
// ## Constants
1914
import { BADGE } from '../../utilities/constants';
2015

16+
import generateId from '../../utilities/generate-id';
17+
2118
/**
2219
* Badges are labels which hold small amounts of information.
2320
*/
2421
class Badge extends React.Component {
2522
constructor(props) {
2623
super(props);
27-
this.generatedId = shortid.generate();
24+
this.generatedId = generateId();
2825
}
2926

3027
/**

components/brand-band/index.jsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@
1111
import React from 'react';
1212
import PropTypes from 'prop-types';
1313

14-
// ### shortid
15-
// [npmjs.com/package/shortid](https://www.npmjs.com/package/shortid)
16-
// shortid is a short, non-sequential, url-friendly, unique id generator
17-
import shortid from 'shortid';
18-
1914
// ### classNames
2015
// [github.com/JedWatson/classnames](https://github.com/JedWatson/classnames)
2116
// A simple javascript utility for conditionally joining classNames together.
@@ -27,6 +22,8 @@ import checkProps from './check-props';
2722
// ## Constants
2823
import { BRAND_BAND } from '../../utilities/constants';
2924

25+
import generateId from '../../utilities/generate-id';
26+
3027
/**
3128
* The brand band provides theming capability that adds personality and improves information density and contrast.
3229
*/
@@ -35,7 +32,7 @@ class BrandBand extends React.Component {
3532
super(props);
3633

3734
checkProps(BRAND_BAND, this.props);
38-
this.generatedId = shortid.generate();
35+
this.generatedId = generateId();
3936
}
4037

4138
getId() {

components/button-group/index.jsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,8 @@ import classNames from 'classnames';
1010

1111
import assign from 'lodash.assign';
1212

13-
// ### shortid
14-
// [npmjs.com/package/shortid](https://www.npmjs.com/package/shortid)
15-
// shortid is a short, non-sequential, url-friendly, unique id generator
16-
import shortid from 'shortid';
17-
1813
import { BUTTON_GROUP } from '../../utilities/constants';
14+
import generateId from '../../utilities/generate-id';
1915

2016
const propTypes = {
2117
/**
@@ -66,7 +62,7 @@ const defaultProps = { labels: {} };
6662
class ButtonGroup extends React.Component {
6763
constructor(props) {
6864
super(props);
69-
this.generatedId = shortid.generate();
65+
this.generatedId = generateId();
7066
}
7167

7268
getId() {

components/card/__docs__/storybook-stories.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from 'react';
22

33
import PropTypes from 'prop-types';
4-
import shortid from 'shortid';
54
import { storiesOf } from '@storybook/react';
65
import { action } from '@storybook/addon-actions';
76
import IconSettings from '../../icon-settings';
@@ -21,6 +20,8 @@ import InlineEdit from '../../forms/input/inline';
2120

2221
import RelatedListWithTable from '../__examples__/related-list-with-table';
2322

23+
import generateId from '../../../utilities/generate-id';
24+
2425
const sampleItems = [
2526
{ id: '0', name: 'Cloudhub' },
2627
{ id: '1', name: 'Cloudhub + Anypoint Connectors' },
@@ -69,7 +70,7 @@ class DemoCard extends React.Component {
6970
this.setState({
7071
items: [
7172
// eslint-disable-next-line no-plusplus
72-
{ id: currentId++, name: `New item #${shortid.generate()}` },
73+
{ id: currentId++, name: `New item #${generateId()}` },
7374
...this.state.items,
7475
],
7576
});

components/carousel/index.jsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@ import React from 'react';
99
import PropTypes from 'prop-types';
1010
import classnames from 'classnames';
1111

12-
// ### shortid
13-
// [npmjs.com/package/shortid](https://www.npmjs.com/package/shortid)
14-
// shortid is a short, non-sequential, url-friendly, unique id generator
15-
import shortid from 'shortid';
16-
1712
import { CAROUSEL } from '../../utilities/constants';
1813

14+
import generateId from '../../utilities/generate-id';
15+
1916
import {
2017
canUseDOM,
2118
canUseEventListeners,
@@ -151,7 +148,7 @@ class Carousel extends React.Component {
151148
translateX: -1000000,
152149
};
153150

154-
this.generatedId = shortid.generate();
151+
this.generatedId = generateId();
155152
}
156153

157154
componentDidMount() {

components/checkbox/index.jsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ import PropTypes from 'prop-types';
1111

1212
import classNames from 'classnames';
1313

14-
// ### shortid
15-
// [npmjs.com/package/shortid](https://www.npmjs.com/package/shortid)
16-
// shortid is a short, non-sequential, url-friendly, unique id generator
17-
import shortid from 'shortid';
18-
1914
// ### Event Helpers
2015
import KEYS from '../../utilities/key-code';
2116
import EventUtil from '../../utilities/event';
@@ -28,6 +23,7 @@ import { CHECKBOX } from '../../utilities/constants';
2823
import Icon from '../icon';
2924

3025
import getAriaProps from '../../utilities/get-aria-props';
26+
import generateId from '../../utilities/generate-id';
3127

3228
const propTypes = {
3329
/**
@@ -211,7 +207,7 @@ class Checkbox extends React.Component {
211207
super(props);
212208

213209
checkProps(CHECKBOX, this.props, componentDoc);
214-
this.generatedId = shortid.generate();
210+
this.generatedId = generateId();
215211
}
216212

217213
getId = () => this.props.id || this.generatedId;

components/color-picker/index.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import classNames from 'classnames';
44
import PropTypes from 'prop-types';
55
import React from 'react';
6-
import shortid from 'shortid';
76
import assign from 'lodash.assign';
87

98
import checkProps from './check-props';
@@ -21,6 +20,7 @@ import Popover from '../popover';
2120
import ColorUtils from '../../utilities/color';
2221

2322
import { COLOR_PICKER } from '../../utilities/constants';
23+
import generateId from '../../utilities/generate-id';
2424

2525
import componentDoc from './component.json';
2626

@@ -247,7 +247,7 @@ class ColorPicker extends React.Component {
247247
constructor(props) {
248248
super(props);
249249

250-
this.generatedId = props.id || shortid.generate();
250+
this.generatedId = props.id || generateId();
251251
const workingColor = ColorUtils.getNewColor(
252252
{
253253
hex: props.valueWorking || props.value,

components/combobox/combobox.jsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import isFunction from 'lodash.isfunction';
1616

1717
import classNames from 'classnames';
1818

19-
import shortid from 'shortid';
20-
2119
import Button from '../button';
2220
import Dialog from '../utilities/dialog';
2321
import InnerInput from '../../components/input/private/inner-input';
@@ -37,6 +35,7 @@ import menuItemSelectScroll from '../../utilities/menu-item-select-scroll';
3735
import checkProps from './check-props';
3836

3937
import { COMBOBOX } from '../../utilities/constants';
38+
import generateId from '../../utilities/generate-id';
4039
import componentDoc from './component.json';
4140
import { IconSettingsContext } from '../icon-settings';
4241

@@ -446,8 +445,8 @@ class Combobox extends React.Component {
446445
// `checkProps` issues warnings to developers about properties (similar to React's built in development tools)
447446
checkProps(COMBOBOX, props, componentDoc);
448447

449-
this.generatedId = shortid.generate();
450-
this.generatedErrorId = shortid.generate();
448+
this.generatedId = generateId();
449+
this.generatedErrorId = generateId();
451450
this.deselectId = `${this.getId()}-deselect`;
452451
}
453452

components/component-docs.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5771,6 +5771,17 @@
57715771
"computed": false
57725772
}
57735773
},
5774+
"disabledSelection": {
5775+
"type": {
5776+
"name": "array"
5777+
},
5778+
"required": false,
5779+
"description": "An array of objects of rows that selection is disabled. See `items` prop for shape of objects.",
5780+
"defaultValue": {
5781+
"value": "[]",
5782+
"computed": false
5783+
}
5784+
},
57745785
"selectRows": {
57755786
"type": {
57765787
"name": "union",

components/data-table/index.jsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@
99
import React from 'react';
1010
import PropTypes from 'prop-types';
1111

12-
// ### shortid
13-
// [npmjs.com/package/shortid](https://www.npmjs.com/package/shortid)
14-
// shortid is a short, non-sequential, url-friendly, unique id generator
15-
import shortid from 'shortid';
16-
1712
import classNames from 'classnames';
1813
import assign from 'lodash.assign';
1914
import isEqual from 'lodash.isequal';
@@ -42,6 +37,7 @@ import Mode from './private/mode';
4237
import Spinner from '../spinner';
4338

4439
import KEYS from '../../utilities/key-code';
40+
import generateId from '../../utilities/generate-id';
4541
import mapKeyEventCallbacks from '../../utilities/key-callbacks';
4642

4743
import {
@@ -404,7 +400,7 @@ class DataTable extends React.Component {
404400

405401
constructor(props) {
406402
super(props);
407-
this.generatedId = shortid.generate();
403+
this.generatedId = generateId();
408404
this.headerRefs = {
409405
action: [],
410406
column: [],
@@ -1229,7 +1225,7 @@ class DataTable extends React.Component {
12291225
const rowId =
12301226
this.getId() && item.id
12311227
? `${this.getId()}-${DATA_TABLE_ROW}-${item.id}`
1232-
: shortid.generate();
1228+
: generateId();
12331229
return this.props.onRenderSubHeadingRow &&
12341230
item.type === 'header-row' ? (
12351231
this.props.onRenderSubHeadingRow({

components/data-table/interactive-element.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
/* eslint-disable fp/no-rest-parameters */
22
import React from 'react';
3-
import shortid from 'shortid';
43
import Mode from './private/mode';
54
import CellContext from './private/cell-context';
65
import TableContext from './private/table-context';
76

7+
import generateId from '../../utilities/generate-id';
8+
89
/**
910
* Wrapper for interactive elements in the table.
1011
*
@@ -21,7 +22,7 @@ export default (WrappedElement) => {
2122
class InteractiveElement extends React.Component {
2223
constructor(props) {
2324
super(props);
24-
this.elementId = shortid.generate();
25+
this.elementId = generateId();
2526
}
2627

2728
onFocus(tableContext, ...args) {

components/date-picker/date-picker.jsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@ import assign from 'lodash.assign';
1313
// joining classNames together."
1414
import classNames from 'classnames';
1515

16-
// ### shortid
17-
// [npmjs.com/package/shortid](https://www.npmjs.com/package/shortid)
18-
// shortid is a short, non-sequential, url-friendly, unique id generator
19-
import shortid from 'shortid';
20-
2116
import Dialog from '../utilities/dialog';
2217
import CalendarWrapper from './private/calendar-wrapper';
2318
import InputIcon from '../icon/input-icon';
@@ -33,6 +28,7 @@ import KEYS from '../../utilities/key-code';
3328
import lowPriorityWarning from '../../utilities/warning/low-priority-warning';
3429

3530
import { DATE_PICKER } from '../../utilities/constants';
31+
import generateId from '../../utilities/generate-id';
3632
import { IconSettingsContext } from '../icon-settings';
3733

3834
const propTypes = {
@@ -273,7 +269,7 @@ class Datepicker extends React.Component {
273269
inputValue: initDate || '',
274270
};
275271

276-
this.generatedId = shortid.generate();
272+
this.generatedId = generateId();
277273

278274
// `checkProps` issues warnings to developers about properties (similar to React's built in development tools)
279275
checkProps(DATE_PICKER, props, componentDoc);

components/docked-composer/index.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import shortid from 'shortid';
43

54
import Button from '../button';
65

6+
import generateId from '../../utilities/generate-id';
7+
78
const propTypes = {
89
/**
910
* HTML id for component.
@@ -71,7 +72,7 @@ const defaultProps = {
7172
class DockedComposer extends React.Component {
7273
constructor(props) {
7374
super(props);
74-
this.generatedId = shortid.generate();
75+
this.generatedId = generateId();
7576
}
7677

7778
getId() {

0 commit comments

Comments
 (0)