@hashicorp/design-system-components 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @hashicorp/design-system-components
2
2
 
3
+ ## 0.11.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#284](https://github.com/hashicorp/design-system/pull/284) [`ba409885`](https://github.com/hashicorp/design-system/commit/ba409885468f91d659d10971fa1f34f64f57ddbc) Thanks [@alex-ju](https://github.com/alex-ju)! - Change focus management in disclosure utility component
8
+
9
+ * [#289](https://github.com/hashicorp/design-system/pull/289) [`bf3a00e5`](https://github.com/hashicorp/design-system/commit/bf3a00e56989bbef92bfa355a42e5775785847a3) Thanks [@didoo](https://github.com/didoo)! - "Alert" and "Toast" components - converted "title" and "description" arguments to be contextual components
10
+
3
11
  ## 0.11.0
4
12
 
5
13
  ### Minor Changes
@@ -0,0 +1 @@
1
+ <div class="hds-alert__description" ...attributes>{{yield}}</div>
@@ -7,12 +7,8 @@
7
7
 
8
8
  <div class="hds-alert__content">
9
9
  <div class="hds-alert__text">
10
- {{#if @title}}
11
- <div class="hds-alert__title">{{@title}}</div>
12
- {{/if}}
13
- {{#if @description}}
14
- <div class="hds-alert__description">{{html-safe @description}}</div>
15
- {{/if}}
10
+ {{yield (hash Title=(component "hds/alert/title"))}}
11
+ {{yield (hash Description=(component "hds/alert/description"))}}
16
12
  </div>
17
13
 
18
14
  <div class="hds-alert__actions">
@@ -0,0 +1 @@
1
+ <div class="hds-alert__title" ...attributes>{{yield}}</div>
@@ -1,16 +1,16 @@
1
- <div class="hds-disclosure" ...attributes>
2
- <div class="hds-disclosure__toggle">
1
+ {{! template-lint-disable no-invalid-interactive }}
2
+ <div
3
+ class="hds-disclosure"
4
+ ...attributes
5
+ {{did-insert this.didInsert}}
6
+ {{on "focusout" this.onFocusOut}}
7
+ {{on "keyup" this.onKeyUp}}
8
+ >
9
+ <div class="hds-disclosure__toggle" tabindex="-1">
3
10
  {{yield (hash onClickToggle=this.onClickToggle isActive=this.isActive) to="toggle"}}
4
11
  </div>
5
12
  {{#if this.isActive}}
6
- <div
7
- class="hds-disclosure__content"
8
- {{focus-trap
9
- isActive=this.isActive
10
- shouldSelfFocus=true
11
- focusTrapOptions=(hash clickOutsideDeactivates=this.clickOutsideDeactivates onDeactivate=this.onDeactivate)
12
- }}
13
- >
13
+ <div class="hds-disclosure__content" tabindex="-1">
14
14
  {{yield to="content"}}
15
15
  </div>
16
16
  {{/if}}
@@ -7,6 +7,11 @@ export default class HdsDisclosureComponent extends Component {
7
7
  @tracked toggleRef;
8
8
  @tracked isToggleClicked;
9
9
 
10
+ @action
11
+ didInsert(element) {
12
+ this.element = element;
13
+ }
14
+
10
15
  @action
11
16
  onClickToggle(event) {
12
17
  // we store a reference to the DOM node that has the "onClickToggle" event associated with it
@@ -14,34 +19,36 @@ export default class HdsDisclosureComponent extends Component {
14
19
  this.toggleRef = event.currentTarget;
15
20
  }
16
21
  this.isActive = !this.isActive;
22
+ // we explicitly apply a focus state to the toggle element to overcome a bug in WebKit (see b8abfcf)
23
+ this.toggleRef.focus();
17
24
  }
18
25
 
19
26
  @action
20
- clickOutsideDeactivates(event) {
21
- // we check if the toggle reference belongs to the tree of parent DOM nodes
22
- // of the element that was clicked and triggered the "click outside" event handling
23
- // notice: we use "event.composedPath" here because is now fully supported (see https://caniuse.com/?search=event%20path)
24
- const path = event.composedPath();
25
- this.isToggleClicked = path.includes(this.toggleRef);
26
- // here we need to return `true` to make sure that the focus trap will be deactivated (and allow the click event to do its thing (i.e. to pass-through to the element that was clicked).
27
- // see: https://github.com/focus-trap/focus-trap#createoptions
28
- return true;
27
+ onFocusOut(event) {
28
+ if (
29
+ !event.relatedTarget || // click or tap a non-related target (e.g. outside the element)
30
+ !this.element.contains(event.relatedTarget) // move focus to a target outside the element
31
+ ) {
32
+ this.deactivate();
33
+ }
29
34
  }
30
35
 
31
36
  @action
32
- onDeactivate() {
33
- // on deactivate we hide the content, except for the case when the button has been clicked
34
- // the reason is that the "onClickToggle" is called in any case (there's no way to block the event)
35
- // so when the user clicks the toggle to close the panel, we let the "onClickToggle" handle the closure
36
- // otherwise we would have two changes of status, this and the toggle, and the panel would remain open
37
- if (!this.isToggleClicked) {
37
+ onKeyUp(event) {
38
+ if (event.key === 'Escape') {
39
+ this.deactivate();
40
+ this.toggleRef.focus();
41
+ }
42
+ }
43
+
44
+ @action
45
+ deactivate() {
46
+ if (this.isActive) {
38
47
  this.isActive = false;
39
- // we need to reset this check
40
- this.isToggleClicked = false;
41
- // we call the "onClose" callback if it exists (and is a function)
42
- if (this.args.onClose && typeof this.args.onClose === 'function') {
43
- this.args.onClose();
44
- }
48
+ }
49
+ // we call the "onClose" callback if it exists (and is a function)
50
+ if (this.args.onClose && typeof this.args.onClose === 'function') {
51
+ this.args.onClose();
45
52
  }
46
53
  }
47
54
  }
@@ -3,13 +3,18 @@
3
3
  @type="inline"
4
4
  @color={{@color}}
5
5
  @icon={{@icon}}
6
- @title={{@title}}
7
- @description={{@description}}
8
6
  @onDismiss={{@onDismiss}}
9
7
  ...attributes
10
8
  as |A|
11
9
  >
12
10
  {{yield
13
- (hash Button=A.Button Link::Standalone=A.Link::Standalone LinkTo::Standalone=A.LinkTo::Standalone Generic=A.Generic)
11
+ (hash
12
+ Title=A.Title
13
+ Description=A.Description
14
+ Button=A.Button
15
+ Link::Standalone=A.Link::Standalone
16
+ LinkTo::Standalone=A.LinkTo::Standalone
17
+ Generic=A.Generic
18
+ )
14
19
  }}
15
20
  </Hds::Alert>
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/alert/description';
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/alert/title';
@@ -69,14 +69,27 @@
69
69
  }
70
70
 
71
71
  code, pre {
72
- font-family: var(--token-typography-font-stack-code);
72
+ background-color: var(--token-color-surface-primary);
73
+ border: 1px solid var(--token-color-palette-neutral-200);
74
+ border-radius: 5px;
75
+ display: inline;
76
+ font-family: var(--token-typography-code-100-font-family);
77
+ font-size: 0.9em; // as discussed with designers, we reduce the size for optical/visual balance
78
+ line-height: 1em;
79
+ padding: 1px 5px;
73
80
  }
74
81
 
75
- // Notice: in the future this may become a "Link::Inline" component (for now we declare the styles directly here)
82
+ // Notice: in the future this may become a "Link::Inline" component (for now we declare directly the same styles here)
76
83
  a {
77
84
  color: var(--token-color-foreground-action);
78
- // At the moment the "focus" state is not well defined in design (the one that is in Figma does not work) so we just apply a simple color to the default outline
79
- outline-color: var(--token-color-focus-action-external);
85
+
86
+ &:focus,
87
+ &.is-focus,
88
+ &:focus-visible {
89
+ outline: 2px solid var(--token-color-focus-action-internal);
90
+ outline-offset: 1px;
91
+ text-decoration: none;
92
+ }
80
93
 
81
94
  &:hover {
82
95
  color: var(--token-color-foreground-action-hover);
@@ -0,0 +1,4 @@
1
+ {{! ADD YOUR TEMPLATE CONTENT HERE }}
2
+ <div className={{this.classNames}} ...attributes>
3
+ {{yield}}
4
+ </div>
@@ -0,0 +1,23 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class Hds<%= classifiedModuleName %>IndexComponent extends Component {
4
+ // UNCOMMENT THIS IF YOU NEED A CONSTRUCTOR
5
+ // constructor() {
6
+ // super(...arguments);
7
+ // // ADD YOUR ASSERTIONS HERE
8
+ // }
9
+
10
+ /**
11
+ * Get the class names to apply to the component.
12
+ * @method classNames
13
+ * @return {string} The "class" attribute to apply to the component.
14
+ */
15
+ get classNames() {
16
+ let classes = ['hds-<%= kebabizedModuleName %>'];
17
+
18
+ // add a class based on the @xxx argument
19
+ // classes.push(`hds-<%= kebabizedModuleName %>--[variant]-${this.xxx}`);
20
+
21
+ return classes.join(' ');
22
+ }
23
+ }
@@ -0,0 +1 @@
1
+ export { default } from '@hashicorp/design-system-components/components/hds/<%= dasherizedModuleName %>/index';
@@ -0,0 +1,6 @@
1
+ //
2
+ // <%= folderizedModuleName %>
3
+ //
4
+ // properties within each class are sorted alphabetically
5
+ //
6
+
@@ -0,0 +1,26 @@
1
+ /* eslint-disable ember/no-string-prototype-extensions */
2
+ /* eslint-disable node/no-extraneous-require */
3
+ 'use strict';
4
+
5
+ const stringUtil = require('ember-cli-string-utils');
6
+
7
+ module.exports = {
8
+ description: 'Generates all files for an HDS component',
9
+
10
+ locals(options) {
11
+ return {
12
+ columnizedModuleName: options.entity.name
13
+ .split('/')
14
+ .map((part) => stringUtil.classify(part))
15
+ .join('::'),
16
+ kebabizedModuleName: options.entity.name
17
+ .split('/')
18
+ .map((part) => stringUtil.dasherize(part))
19
+ .join('-'),
20
+ folderizedModuleName: options.entity.name
21
+ .split('/')
22
+ .map((part) => stringUtil.dasherize(part).toUpperCase())
23
+ .join(' > '),
24
+ };
25
+ },
26
+ };
@@ -0,0 +1,3 @@
1
+ import Route from '@ember/routing/route';
2
+
3
+ export default class Components<%= classifiedModuleName %>Route extends Route {}
@@ -0,0 +1,65 @@
1
+ {{page-title "<%= columnizedModuleName %> Component"}}
2
+
3
+ <h2 class="dummy-h2"><%= columnizedModuleName %></h2>
4
+
5
+ {{! ADD YOUR CONTENT BELOW }}
6
+ {{! Please follow as much as possible what it's already done in other components. }}
7
+ {{! You can start by copy&pasting it, if you want, and then adapt it according to your needs. }}
8
+ {{! Once done, please remove these comments (they're created by the generator). }}
9
+
10
+ <section>
11
+ <h3 class="dummy-h3" id="overview"><a href="#overview">§</a> Overview</h3>
12
+
13
+ {{! ADD YOUR CONTENT HERE }}
14
+
15
+ </section>
16
+
17
+ <section>
18
+ <h3 class="dummy-h3" id="component-api"><a href="#component-api">§</a> Component API</h3>
19
+ <p class="dummy-paragraph" id="component-api-<%= kebabizedModuleName %>">
20
+ Here is the API for the component:
21
+ </p>
22
+ <dl class="dummy-component-props" aria-labelledby="component-api-<%= kebabizedModuleName %>">
23
+
24
+ {{! ADD YOUR CONTENT HERE }}
25
+
26
+ </dl>
27
+ </section>
28
+
29
+ <section>
30
+ <h3 class="dummy-h3" id="how-to-use"><a href="#how-to-use" class="dummy-link-section">§</a> How to use</h3>
31
+
32
+ {{! ADD YOUR CONTENT HERE }}
33
+
34
+ </section>
35
+
36
+ <section>
37
+ <h3 class="dummy-h3" id="design-guidelines"><a href="#design-guidelines" class="dummy-link-section">§</a>Design guidelines</h3>
38
+ {{! UNCOMMENT THIS BLOCK (once the link and/or the image are available) }}
39
+ {{!
40
+ <div class="dummy-design-guidelines">
41
+ <p class="dummy-paragraph">
42
+ <a href="[ADD THE LINK TO THE FIGMA FILE/PAGE HERE!]" target="_blank" rel="noopener noreferrer">Figma UI Kit</a>
43
+ </p>
44
+ <br />
45
+ <img class="dummy-figma-docs" src="/assets/images/<%= kebabizedModuleName %>-design-usage.png" alt="" role="none" />
46
+ </div>
47
+ }}
48
+ </section>
49
+
50
+ <section>
51
+ <h3 class="dummy-h3" id="accessibility"><a href="#accessibility" class="dummy-link-section">§</a> Accessibility</h3>
52
+
53
+ {{! ADD YOUR CONTENT HERE }}
54
+
55
+ </section>
56
+
57
+ <section data-test-percy>
58
+ <h3 class="dummy-h3" id="showcase"><a href="#showcase" class="dummy-link-section">§</a> Showcase</h3>
59
+
60
+ {{! ADD YOUR CONTENT HERE }}
61
+
62
+ {{! This below is just an example of invocation, to get started }}
63
+ <Hds::<%= columnizedModuleName %>>This is the Hds::<%= columnizedModuleName %> component </Hds::<%= columnizedModuleName %>>
64
+
65
+ </section>
@@ -0,0 +1,17 @@
1
+ import { module, test } from 'qunit';
2
+ import { setupRenderingTest } from 'ember-qunit';
3
+ import { render } from '@ember/test-helpers';
4
+ import { hbs } from 'ember-cli-htmlbars';
5
+
6
+ module('Integration | Component | hds/<%= dasherizedModuleName %>/index', function (hooks) {
7
+ setupRenderingTest(hooks);
8
+
9
+ test('it renders the component', async function (assert) {
10
+ await render(hbs`<Hds::<%= columnizedModuleName %> />`);
11
+ assert.dom(this.element).exists();
12
+ });
13
+ test('it should render with a CSS class that matches the component name', async function (assert) {
14
+ await render(hbs`<Hds::<%= columnizedModuleName %> id="test-<%= kebabizedModuleName %>" />`);
15
+ assert.dom('#test-<%= kebabizedModuleName %>').hasClass('hds-<%= kebabizedModuleName %>');
16
+ });
17
+ });
@@ -0,0 +1,126 @@
1
+ /* eslint-disable ember/no-string-prototype-extensions */
2
+ /* eslint-disable node/no-extraneous-require */
3
+ 'use strict';
4
+
5
+ const stringUtil = require('ember-cli-string-utils');
6
+ const EmberRouterGenerator = require('ember-router-generator');
7
+ const fs = require('fs');
8
+
9
+ module.exports = {
10
+ description: 'Generates tests for an HDS component',
11
+
12
+ locals(options) {
13
+ return {
14
+ columnizedModuleName: getColumnizedModuleName(options.entity.name),
15
+ kebabizedModuleName: getKebabizedModuleName(options.entity.name),
16
+ folderizedModuleName: getFolderizedModuleName(options.entity.name),
17
+ };
18
+ },
19
+
20
+ fileMapTokens() {
21
+ return {
22
+ // prepend `db-` to the file name
23
+ __dummyCSSFileName__(options) {
24
+ return getDummyCSSFileName(options.dasherizedModuleName);
25
+ },
26
+ };
27
+ },
28
+
29
+ afterInstall(options) {
30
+ updateDummyAppRouter.call(this, options);
31
+ updateDummyAppCSS.call(this, options);
32
+ updateDummyAppIndexHBS.call(this, options);
33
+ },
34
+ };
35
+
36
+ const updateDummyAppRouter = (options) => {
37
+ const newRouteToAdd = `components/${options.entity.name}`; // we prefix all the component routes with "components"
38
+ const routerFilePath = `${options.project.root}/tests/dummy/app/router.js`;
39
+ const source = fs.readFileSync(routerFilePath, 'utf-8');
40
+ let oldRoutes = new EmberRouterGenerator(source);
41
+ let newRoutes = oldRoutes['add'](newRouteToAdd, options);
42
+ fs.writeFileSync(routerFilePath, newRoutes.code());
43
+ };
44
+
45
+ const updateDummyAppCSS = (options) => {
46
+ const name = options.entity.name;
47
+ const cssFilePath = `${options.project.root}/tests/dummy/app/styles/app.scss`;
48
+ const source = fs.readFileSync(cssFilePath, 'utf-8');
49
+ const oldLinesArray = source.split(/\r?\n/);
50
+ const firstComponentImportIndex =
51
+ oldLinesArray.findIndex((line) =>
52
+ line.match(/^\/\/ START COMPONENT PAGES IMPORTS/)
53
+ ) + 1;
54
+ const lastComponentImportIndex =
55
+ oldLinesArray.findIndex((line) =>
56
+ line.match(/^\/\/ END COMPONENT PAGES IMPORTS/)
57
+ ) - 1;
58
+ const importLinesArray = oldLinesArray.slice(
59
+ firstComponentImportIndex,
60
+ lastComponentImportIndex + 1
61
+ );
62
+ importLinesArray.push(`@import "./pages/${getDummyCSSFileName(name)}";`);
63
+ const newImportLinesArray = importLinesArray
64
+ .filter((line, index, self) => self.indexOf(line) === index)
65
+ .sort();
66
+ const newLinesArray = [].concat(
67
+ oldLinesArray.slice(0, firstComponentImportIndex),
68
+ newImportLinesArray,
69
+ oldLinesArray.slice(lastComponentImportIndex + 1)
70
+ );
71
+ fs.writeFileSync(cssFilePath, newLinesArray.join('\n'));
72
+ };
73
+
74
+ const updateDummyAppIndexHBS = (options) => {
75
+ const name = options.entity.name;
76
+ const hbsFilePath = `${options.project.root}/tests/dummy/app/templates/index.hbs`;
77
+ let newListItemHTML = '';
78
+ newListItemHTML += '<!-- MOVE THIS HTML BLOCK IN THE RIGHT POSITION -->\n';
79
+ newListItemHTML += '<!-- (adjust component name & route if necessary) -->\n';
80
+ newListItemHTML += '<li class="dummy-paragraph">\n';
81
+ newListItemHTML += ` <LinkTo @route="components.${getRoutedModuleName(
82
+ name
83
+ )}">\n`;
84
+ newListItemHTML += ` ${getColumnizedModuleName(name)}\n`;
85
+ newListItemHTML += ' </LinkTo>\n';
86
+ newListItemHTML += '</li>\n';
87
+ fs.appendFileSync(hbsFilePath, `\n\n${newListItemHTML}\n`);
88
+ };
89
+
90
+ const getColumnizedModuleName = (name) => {
91
+ const columnizedModuleName = name
92
+ .split('/')
93
+ .map((part) => stringUtil.classify(part))
94
+ .join('::');
95
+ return columnizedModuleName;
96
+ };
97
+
98
+ const getKebabizedModuleName = (name) => {
99
+ const kebabizedModuleName = name
100
+ .split('/')
101
+ .map((part) => stringUtil.dasherize(part))
102
+ .join('-');
103
+ return kebabizedModuleName;
104
+ };
105
+
106
+ const getFolderizedModuleName = (name) => {
107
+ const folderizedModuleName = name
108
+ .split('/')
109
+ .map((part) => stringUtil.dasherize(part).toUpperCase())
110
+ .join(' > ');
111
+ return folderizedModuleName;
112
+ };
113
+
114
+ const getRoutedModuleName = (name) => {
115
+ const routedModuleName = name
116
+ .split('/')
117
+ .map((part) => stringUtil.dasherize(part))
118
+ .join('.');
119
+ return routedModuleName;
120
+ };
121
+
122
+ const getDummyCSSFileName = (name) => {
123
+ const parts = name.split('/');
124
+ const fileName = `db-${parts.pop()}`;
125
+ return `${parts.concat([fileName]).join('/')}`;
126
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashicorp/design-system-components",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "HashiCorp Design System Components",
5
5
  "keywords": [
6
6
  "hashicorp",
@@ -40,7 +40,6 @@
40
40
  "ember-cli-babel": "^7.26.11",
41
41
  "ember-cli-htmlbars": "^6.0.1",
42
42
  "ember-cli-sass": "^10.0.1",
43
- "ember-focus-trap": "^1.0.1",
44
43
  "ember-keyboard": "^8.1.0",
45
44
  "ember-named-blocks-polyfill": "^0.2.5",
46
45
  "sass": "^1.43.4"
@@ -51,7 +50,7 @@
51
50
  "@embroider/test-setup": "^1.5.0",
52
51
  "@glimmer/component": "^1.0.4",
53
52
  "@glimmer/tracking": "^1.0.4",
54
- "@percy/cli": "^1.0.1",
53
+ "@percy/cli": "^1.2.1",
55
54
  "@percy/ember": "^3.0.0",
56
55
  "babel-eslint": "^10.1.0",
57
56
  "broccoli-asset-rev": "^3.0.0",