diff --git a/docs/guides/components.md b/docs/guides/components.md
index 2105a55f00..cd4c4cd741 100644
--- a/docs/guides/components.md
+++ b/docs/guides/components.md
@@ -63,9 +63,10 @@ console.log(button.el());
The above code will output
```html
-
-
Button
-
+
```
Adding the new button to the player
@@ -79,6 +80,30 @@ console.log(button.el());
// will have the same html result as the previous example
```
+The text of the button can be set as an option:
+
+```js
+const myButton = player.addChild('button', {controlText: 'abc'});
+```
+
+or set later:
+
+```js
+myButton.controlText('def');
+```
+
+The control text of a button is normally not visible (but present for screen readers) as the default buttons all display only an icon. The text can be displayed by adding a `vjs-text-visible` class to the button. This or any other class may be set as a setup option, or later by API.
+
+```js
+const myButton = player.addChild('button', {className: 'vjs-text-visible'});
+```
+
+or set later:
+
+```js
+myButton.addClass('vjs-text-visible');
+```
+
## Component Children
Again, refer to [the component API docs][api] for complete details on methods available for managing component structures.
diff --git a/src/css/components/_control.scss b/src/css/components/_control.scss
index 7d1fba42f1..4511768140 100644
--- a/src/css/components/_control.scss
+++ b/src/css/components/_control.scss
@@ -9,8 +9,14 @@
height: 100%;
width: 4em;
@include flex(none);
+}
+.video-js .vjs-control.vjs-visible-text {
+ width: auto;
+ padding-left: 1em;
+ padding-right: 1em;
}
+
.vjs-button > .vjs-icon-placeholder:before {
font-size: 1.8em;
line-height: 1.67;
@@ -30,7 +36,7 @@
}
// Hide control text visually, but have it available for screenreaders
-.video-js .vjs-control-text {
+.video-js *:not(.vjs-visible-text) > .vjs-control-text {
@include hide-visually;
}
diff --git a/src/js/clickable-component.js b/src/js/clickable-component.js
index f9d5021bad..ba7410cae5 100644
--- a/src/js/clickable-component.js
+++ b/src/js/clickable-component.js
@@ -22,14 +22,26 @@ class ClickableComponent extends Component {
* The `Player` that this class should be attached to.
*
* @param {Object} [options]
- * The key/value store of player options.
+ * The key/value store of component options.
*
* @param {function} [options.clickHandler]
* The function to call when the button is clicked / activated
+ *
+ * @param {string} [options.controlText]
+ * The text to set on the button
+ *
+ * @param {string} [options.className]
+ * A class or space separated list of classes to add the component
+ *
*/
constructor(player, options) {
+
super(player, options);
+ if (this.options_.controlText) {
+ this.controlText(this.options_.controlText);
+ }
+
this.handleMouseOver_ = (e) => this.handleMouseOver(e);
this.handleMouseOut_ = (e) => this.handleMouseOut(e);
this.handleClick_ = (e) => this.handleClick(e);
diff --git a/src/js/component.js b/src/js/component.js
index 9e9c28b023..728a51c77c 100644
--- a/src/js/component.js
+++ b/src/js/component.js
@@ -41,13 +41,16 @@ class Component {
* The `Player` that this class should be attached to.
*
* @param {Object} [options]
- * The key/value store of player options.
+ * The key/value store of component options.
*
* @param {Object[]} [options.children]
* An array of children objects to intialize this component with. Children objects have
* a name property that will be used if more than one component of the same type needs to be
* added.
*
+ * @param {string} [options.className]
+ * A class or space separated list of classes to add the component
+ *
* @param {Component~ReadyCallback} [ready]
* Function that gets called when the `Component` is ready.
*/
@@ -91,6 +94,10 @@ class Component {
this.el_ = this.createEl();
}
+ if (options.className && this.el_) {
+ options.className.split(' ').forEach(c => this.addClass(c));
+ }
+
// if evented is anything except false, we want to mixin in evented
if (options.evented !== false) {
// Make this an evented object and use `el_`, if available, as its event bus
diff --git a/test/unit/clickable-component.test.js b/test/unit/clickable-component.test.js
index fcb04fcc4f..12f5d67fb5 100644
--- a/test/unit/clickable-component.test.js
+++ b/test/unit/clickable-component.test.js
@@ -140,3 +140,17 @@ QUnit.test('language change should localize its text', function(assert) {
testClickableComponent.dispose();
player.dispose();
});
+
+QUnit.test('class and text should be settable from options', function(assert) {
+ const player = TestHelpers.makePlayer({});
+ const testClickableComponent = new ClickableComponent(player, {
+ className: 'class1',
+ controlText: 'some text'
+ });
+
+ assert.equal(testClickableComponent.controlText(), 'some text', 'text was set');
+ assert.ok(testClickableComponent.hasClass('class1'), 'class was set');
+
+ testClickableComponent.dispose();
+ player.dispose();
+});
diff --git a/test/unit/component.test.js b/test/unit/component.test.js
index 4865ad29a2..793ea73444 100644
--- a/test/unit/component.test.js
+++ b/test/unit/component.test.js
@@ -677,6 +677,21 @@ QUnit.test('should add and remove a CSS class', function(assert) {
comp.dispose();
});
+QUnit.test('should add CSS class passed in options', function(assert) {
+ const comp = new Component(this.player, {className: 'class1 class2'});
+
+ assert.ok(comp.el().className.indexOf('class1') !== -1, 'first of multiple classes added');
+ assert.ok(comp.el().className.indexOf('class2') !== -1, 'second of multiple classes added');
+
+ comp.dispose();
+
+ const comp2 = new Component(this.player, {className: 'class1'});
+
+ assert.ok(comp2.el().className.indexOf('class1') !== -1, 'singe class added');
+
+ comp2.dispose();
+});
+
QUnit.test('should show and hide an element', function(assert) {
const comp = new Component(this.player, {});