@automattic/vip-design-system 2.3.1 → 2.4.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.
@@ -24,7 +24,7 @@ var NavBase = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref) {
24
24
  "aria-label": label,
25
25
  ref: ref,
26
26
  className: (0, _classnames["default"])(VIP_NAV, className),
27
- sx: (0, _styles.navStyles)(variant),
27
+ sx: (0, _styles.navStyles)(variant, orientation),
28
28
  orientation: orientation,
29
29
  children: (0, _jsxRuntime.jsx)(NavigationMenu.List, {
30
30
  className: (0, _classnames["default"])(VIP_NAV + "-list"),
@@ -68,22 +68,34 @@ var Tab = exports.Tab = {
68
68
  },
69
69
  label: "Nav Tab",
70
70
  children: [(0, _jsxRuntime.jsx)(_NavItem.NavItem.Tab, {
71
- active: true,
72
71
  as: _CustomLink.CustomLink,
73
- href: "https://random-website.com/",
72
+ href: "#!",
74
73
  children: "PHP"
75
74
  }), (0, _jsxRuntime.jsx)(_NavItem.NavItem.Tab, {
76
75
  as: _CustomLink.CustomLink,
77
- href: "https://wordpress.com",
76
+ href: "#2!",
78
77
  children: "WordPress"
79
78
  }), (0, _jsxRuntime.jsx)(_NavItem.NavItem.Tab, {
79
+ active: true,
80
80
  as: _CustomLink.CustomLink,
81
- href: "https://newrelic.com/",
81
+ href: "#3!",
82
82
  children: "New Relic"
83
+ }), (0, _jsxRuntime.jsx)(_NavItem.NavItem.Tab, {
84
+ as: _CustomLink.CustomLink,
85
+ href: "#4!",
86
+ children: "Datadog"
87
+ }), (0, _jsxRuntime.jsx)(_NavItem.NavItem.Tab, {
88
+ as: _CustomLink.CustomLink,
89
+ href: "#4!",
90
+ children: "OnePlus"
91
+ }), (0, _jsxRuntime.jsx)(_NavItem.NavItem.Tab, {
92
+ as: _CustomLink.CustomLink,
93
+ href: "#5!",
94
+ children: "Rollbar"
83
95
  }), (0, _jsxRuntime.jsx)(_NavItem.NavItem.Tab, {
84
96
  disabled: true,
85
97
  as: _CustomLink.CustomLink,
86
- href: "https://google.com/",
98
+ href: "#6!",
87
99
  children: "Not accessible"
88
100
  })]
89
101
  })]
@@ -131,6 +143,7 @@ var Menu = exports.Menu = {
131
143
  children: "Domains & TLS"
132
144
  }), (0, _jsxRuntime.jsxs)(_NavItem.NavItem.MenuGroup, {
133
145
  active: true,
146
+ activeChildren: true,
134
147
  label: "Logs",
135
148
  renderIcon: function renderIcon(size) {
136
149
  return (0, _jsxRuntime.jsx)(_bi.BiHistory, {
@@ -3,6 +3,7 @@ import { NavItemRenderIconProp } from './Nav';
3
3
  import { NavItemBaseProps } from './NavItem';
4
4
  export interface NavItemGroupProps extends NavItemBaseProps {
5
5
  renderIcon?: NavItemRenderIconProp;
6
+ activeChildren?: boolean;
6
7
  label: string;
7
8
  }
8
9
  export declare const IconContainer: ({ children }: {
@@ -23,6 +23,7 @@ var NavItemGroupBase = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref)
23
23
  orientation = _ref.orientation,
24
24
  className = _ref.className,
25
25
  active = _ref.active,
26
+ activeChildren = _ref.activeChildren,
26
27
  renderIcon = _ref.renderIcon,
27
28
  children = _ref.children,
28
29
  sx = _ref.sx;
@@ -47,6 +48,7 @@ var NavItemGroupBase = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref)
47
48
  "aria-haspopup": true,
48
49
  "data-active": active || undefined,
49
50
  "data-open": isExpanded || undefined,
51
+ "data-active-children": activeChildren || undefined,
50
52
  sx: _extends({}, (0, _styles.navItemLinkVariantStyles)(variant), _menugroup.navItemGroupTriggerStyles),
51
53
  children: [renderIcon ? (0, _jsxRuntime.jsx)(IconContainer, {
52
54
  children: renderIcon(_NavItem.NAV_ITEM_ICON_SIZE)
@@ -0,0 +1,2 @@
1
+ /** @jsxImportSource theme-ui */
2
+ export {};
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+
3
+ var _react = require("@testing-library/react");
4
+ var _jestAxe = require("jest-axe");
5
+ var _themeUi = require("theme-ui");
6
+ var _Nav = require("./Nav");
7
+ var _NavItem = require("./NavItem");
8
+ var _ = require("../");
9
+ var _CustomLink = require("../utils/stories/CustomLink");
10
+ var _jsxRuntime = require("theme-ui/jsx-runtime");
11
+ function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == typeof h && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator["return"] && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(typeof e + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, "catch": function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
12
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
13
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } /** @jsxImportSource theme-ui */ /* eslint-disable @typescript-eslint/ban-ts-comment */ // @ts-nocheck
14
+ var renderWithTheme = function renderWithTheme(children) {
15
+ return (0, _react.render)((0, _jsxRuntime.jsx)(_themeUi.ThemeUIProvider, {
16
+ theme: _.theme,
17
+ children: children
18
+ }));
19
+ };
20
+ var renderComponent = function renderComponent() {
21
+ return renderWithTheme((0, _jsxRuntime.jsx)(_Nav.Nav.Menu, {
22
+ label: "Nav Menu",
23
+ children: (0, _jsxRuntime.jsxs)(_NavItem.NavItem.MenuGroup, {
24
+ active: true,
25
+ activeChildren: true,
26
+ label: "Logs",
27
+ children: [(0, _jsxRuntime.jsx)(_NavItem.NavItem.Menu, {
28
+ active: true,
29
+ as: _CustomLink.CustomLink,
30
+ href: "https://google.com/",
31
+ children: "Audit"
32
+ }), (0, _jsxRuntime.jsx)(_NavItem.NavItem.Menu, {
33
+ as: _CustomLink.CustomLink,
34
+ href: "https://wpvip.com/",
35
+ children: "Runtime"
36
+ }), (0, _jsxRuntime.jsx)(_NavItem.NavItem.Menu, {
37
+ as: _CustomLink.CustomLink,
38
+ href: "https://dashboard.wpvip.com/",
39
+ children: "Slow Query"
40
+ })]
41
+ })
42
+ }));
43
+ };
44
+ describe('<NavItemGroup />', function () {
45
+ it('renders the NavItemGroup component a data-active-children', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
46
+ var _renderComponent, container, button;
47
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
48
+ while (1) switch (_context.prev = _context.next) {
49
+ case 0:
50
+ _renderComponent = renderComponent(), container = _renderComponent.container; // Should find the button label
51
+ button = _react.screen.getByRole('button', {
52
+ label: /Logs/
53
+ });
54
+ expect(button).toBeInTheDocument();
55
+
56
+ // Expect to have another attribute
57
+ expect(button).toHaveAttribute('data-active', 'true');
58
+ expect(button).toHaveAttribute('data-active-children', 'true');
59
+
60
+ // Check for accessibility issues
61
+ _context.t0 = expect;
62
+ _context.next = 8;
63
+ return (0, _jestAxe.axe)(container);
64
+ case 8:
65
+ _context.t1 = _context.sent;
66
+ (0, _context.t0)(_context.t1).toHaveNoViolations();
67
+ case 10:
68
+ case "end":
69
+ return _context.stop();
70
+ }
71
+ }, _callee);
72
+ })));
73
+ });
@@ -4,13 +4,14 @@ type MixedStyleProp = {
4
4
  [key: string]: string | number | MixedStyleProp | ThemeUIStyleObject;
5
5
  };
6
6
  export declare const menuItemStyles: (orientation: NavProps['orientation']) => ThemeUIStyleObject;
7
+ export declare const tabItemStyles: (orientation: NavProps['orientation']) => ThemeUIStyleObject;
7
8
  export declare const menuInverseItemStyles: (orientation: NavProps['orientation']) => ThemeUIStyleObject;
8
9
  export declare const menuItemLinkStyles: MixedStyleProp;
9
10
  export declare const menuInverseItemLinkStyles: {
10
11
  backgroundColor: string;
11
12
  color: string;
12
13
  "&:visited": {};
13
- "&[data-active]": {};
14
- "&:focus:not(&[data-active]), &:hover:not(&[data-active])": {};
14
+ "&[data-active]:not(&[data-active-children=\"true\"][data-state=\"open\"])": {};
15
+ "&:focus:not(&[data-active]:not(&[data-active-children=\"true\"][data-state=\"open\"])), &:hover:not(&[data-active]:not(&[data-active-children=\"true\"][data-state=\"open\"]))": {};
15
16
  };
16
17
  export {};
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.menuItemStyles = exports.menuItemLinkStyles = exports.menuInverseItemStyles = exports.menuInverseItemLinkStyles = void 0;
4
+ exports.tabItemStyles = exports.menuItemStyles = exports.menuItemLinkStyles = exports.menuInverseItemStyles = exports.menuInverseItemLinkStyles = void 0;
5
5
  var _primary = require("./primary");
6
6
  var _menuItemLinkStyles, _extends2;
7
7
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
@@ -16,6 +16,13 @@ var menuItemStyles = exports.menuItemStyles = function menuItemStyles(orientatio
16
16
  });
17
17
  };
18
18
 
19
+ // Tab Item Style <li>
20
+ var tabItemStyles = exports.tabItemStyles = function tabItemStyles(orientation) {
21
+ return _extends({}, (0, _primary.defaultNavItemStyles)(orientation), {
22
+ mr: 0
23
+ });
24
+ };
25
+
19
26
  // Menu Inverse Item Style <li>
20
27
  var menuInverseItemStyles = exports.menuInverseItemStyles = function menuInverseItemStyles(orientation) {
21
28
  return _extends({}, menuItemStyles(orientation), {
@@ -29,9 +36,9 @@ var menuInverseItemStyles = exports.menuInverseItemStyles = function menuInverse
29
36
  // Menu Item Link <a>
30
37
 
31
38
  var visitedLink = '&:visited';
32
- var activeAfter = '&[data-active]::before';
33
- var active = '&[data-active]';
34
- var focusNotActiveHoverNotActive = '&:focus:not(&[data-active]), &:hover:not(&[data-active])';
39
+ var active = '&[data-active]:not(&[data-active-children="true"][data-state="open"])';
40
+ var activeBefore = active + "::before";
41
+ var focusNotActiveHoverNotActive = "&:focus:not(" + active + "), &:hover:not(" + active + ")";
35
42
  var notHover = ':not(&:hover)';
36
43
  var svgIcon = 'svg';
37
44
  var menuItemLinkStyles = exports.menuItemLinkStyles = (_menuItemLinkStyles = {
@@ -59,7 +66,7 @@ var menuItemLinkStyles = exports.menuItemLinkStyles = (_menuItemLinkStyles = {
59
66
  width: '100%'
60
67
  }, _menuItemLinkStyles[visitedLink] = {
61
68
  color: 'text'
62
- }, _menuItemLinkStyles[activeAfter] = {
69
+ }, _menuItemLinkStyles[activeBefore] = {
63
70
  position: 'absolute',
64
71
  content: "''",
65
72
  overflow: 'hidden',
@@ -1,3 +1,4 @@
1
1
  import { ThemeUIStyleObject } from 'theme-ui';
2
- export declare const tabRootStyles: ThemeUIStyleObject;
2
+ import { NavProps } from '../../Nav';
3
+ export declare const tabRootStyles: (orientation: NavProps['orientation']) => ThemeUIStyleObject;
3
4
  export declare const tabItemLinkStyles: ThemeUIStyleObject;
@@ -5,27 +5,61 @@ exports.tabRootStyles = exports.tabItemLinkStyles = void 0;
5
5
  var _primary = require("./primary");
6
6
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
7
7
  // Tab Root Styles <nav>
8
-
9
- var tabRootStyles = exports.tabRootStyles = {
10
- width: '100%',
11
- borderColor: 'borders.2'
8
+ var getTabPropsByOrientation = function getTabPropsByOrientation(orientation) {
9
+ if (orientation === 'vertical') {
10
+ return {
11
+ '> div:first-of-type': {
12
+ height: '100%',
13
+ overflowY: 'auto'
14
+ },
15
+ ul: {
16
+ minHeight: 'max-content'
17
+ }
18
+ };
19
+ }
20
+ return {
21
+ '> div:first-of-type': {
22
+ width: '100%',
23
+ overflowX: 'auto'
24
+ },
25
+ ul: {
26
+ minWidth: 'max-content'
27
+ }
28
+ };
29
+ };
30
+ var tabRootStyles = exports.tabRootStyles = function tabRootStyles(orientation) {
31
+ return _extends({
32
+ width: '100%',
33
+ borderColor: 'borders.2',
34
+ gap: 2
35
+ }, getTabPropsByOrientation(orientation));
12
36
  };
13
37
 
14
38
  // Tab Link <a>
15
39
  var tabItemLinkStyles = exports.tabItemLinkStyles = _extends({}, _primary.defaultItemLinkStyles, {
16
- px: 0,
17
- mr: 2,
40
+ px: 2,
41
+ height: '100%',
42
+ backgroundColor: 'red',
18
43
  color: 'heading',
19
44
  '&[data-active]': {
20
45
  color: 'link',
21
46
  fontWeight: 'normal',
22
- boxShadow: 'inset 0 -1px 0 0, 0 1px 0 0'
47
+ position: 'relative',
48
+ '&::after': {
49
+ position: 'absolute',
50
+ bottom: 0,
51
+ display: 'block',
52
+ width: '100%',
53
+ content: '""',
54
+ height: '0.125rem',
55
+ backgroundColor: 'link'
56
+ }
23
57
  },
24
58
  '&[aria-disabled="true"]': {
25
59
  color: 'muted'
26
60
  },
27
61
  ':hover': {
28
62
  fontWeight: 'regular',
29
- color: 'heading'
63
+ color: 'link'
30
64
  }
31
65
  });
@@ -3,6 +3,5 @@ import { NavProps, NavVariant } from './Nav';
3
3
  export declare const navItemLinkVariantStyles: (variant: NavVariant) => ThemeUIStyleObject;
4
4
  export declare const navItemLinkStyles: (variant: NavVariant) => ThemeUIStyleObject;
5
5
  export declare const navItemStyles: (orientation: NavProps['orientation'], variant?: NavVariant) => ThemeUIStyleObject;
6
- export declare const navRootStyles: (variant: NavVariant) => ThemeUIStyleObject;
7
- export declare const navStyles: (variant: NavVariant) => ThemeUIStyleObject;
6
+ export declare const navStyles: (variant: NavVariant, orientation: NavProps['orientation']) => ThemeUIStyleObject;
8
7
  export declare const navMenuListStyles: (orientation: NavProps['orientation']) => ThemeUIStyleObject;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.navStyles = exports.navRootStyles = exports.navMenuListStyles = exports.navItemStyles = exports.navItemLinkVariantStyles = exports.navItemLinkStyles = void 0;
4
+ exports.navStyles = exports.navMenuListStyles = exports.navItemStyles = exports.navItemLinkVariantStyles = exports.navItemLinkStyles = void 0;
5
5
  var _breadcrumbs = require("./styles/variants/breadcrumbs");
6
6
  var _menu = require("./styles/variants/menu");
7
7
  var _primary = require("./styles/variants/primary");
@@ -57,6 +57,10 @@ var navItemStyles = exports.navItemStyles = function navItemStyles(orientation,
57
57
  {
58
58
  return (0, _menu.menuItemStyles)(orientation);
59
59
  }
60
+ case 'tabs':
61
+ {
62
+ return (0, _menu.tabItemStyles)(orientation);
63
+ }
60
64
  case 'menu-inverse':
61
65
  {
62
66
  return (0, _menu.menuInverseItemStyles)(orientation);
@@ -71,28 +75,12 @@ var navItemStyles = exports.navItemStyles = function navItemStyles(orientation,
71
75
  }
72
76
  }
73
77
  };
74
- var navRootStyles = exports.navRootStyles = function navRootStyles(variant) {
75
- switch (variant) {
76
- case 'tabs':
77
- {
78
- return _tabs.tabRootStyles;
79
- }
80
- case 'toolbar':
81
- {
82
- return _toolbar.toolbarRootStyles;
83
- }
84
- default:
85
- {
86
- return _primary.defaultNavRootStyles;
87
- }
88
- }
89
- };
90
- var navStyles = exports.navStyles = function navStyles(variant) {
78
+ var navStyles = exports.navStyles = function navStyles(variant, orientation) {
91
79
  var navStyle = {};
92
80
  switch (variant) {
93
81
  case 'tabs':
94
82
  {
95
- navStyle = _tabs.tabRootStyles;
83
+ navStyle = (0, _tabs.tabRootStyles)(orientation);
96
84
  }
97
85
  break;
98
86
  case 'toolbar':
@@ -38,6 +38,7 @@ export const Default = () => (
38
38
  </TabsContent>
39
39
  </Tabs>
40
40
  );
41
+
41
42
  export const SetActiveTab = () => {
42
43
  const [ activeTab, setActiveTab ] = React.useState( 'all' );
43
44
 
@@ -24,7 +24,16 @@ const styles = {
24
24
  '&[data-state="active"]': {
25
25
  color: 'link',
26
26
  fontWeight: 'regular',
27
- boxShadow: 'inset 0 -1px 0 0, 0 1px 0 0',
27
+ position: 'relative',
28
+ '&::after': {
29
+ position: 'absolute',
30
+ bottom: 0,
31
+ display: 'block',
32
+ width: '100%',
33
+ content: '""',
34
+ height: '0.125rem',
35
+ backgroundColor: 'link',
36
+ },
28
37
  },
29
38
  '&:disabled': {
30
39
  color: 'muted',
@@ -326,6 +326,7 @@ declare namespace _default {
326
326
  '&:hover': {
327
327
  textDecorationLine: string;
328
328
  textDecorationThickness: string;
329
+ textUnderlineOffset: string;
329
330
  };
330
331
  };
331
332
  export namespace svg {
@@ -414,8 +414,10 @@ export default {
414
414
  '&:active': {
415
415
  color: 'links.active',
416
416
  },
417
+
417
418
  textDecorationThickness: '0.125rem',
418
- textUnderlineOffset: '0.125rem',
419
+ textUnderlineOffset: '0.250rem',
420
+
419
421
  '&:hover, &:focus': {
420
422
  color: 'links.hover',
421
423
  textDecorationLine: 'underline',
@@ -534,7 +536,8 @@ export default {
534
536
  a: {
535
537
  '&:hover': {
536
538
  textDecorationLine: 'underline',
537
- textDecorationThickness: '2px',
539
+ textDecorationThickness: '0.125rem',
540
+ textUnderlineOffset: '0.250rem',
538
541
  },
539
542
  },
540
543
  svg: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip-design-system",
3
- "version": "2.3.1",
3
+ "version": "2.4.1",
4
4
  "main": "build/system/index.js",
5
5
  "scripts": {
6
6
  "build-storybook": "storybook build",
@@ -115,16 +115,25 @@ export const Tab: Story = {
115
115
  <strong>Variant: Tab</strong>
116
116
  </p>
117
117
  <Nav.Tab sx={ { mb: 4 } } label="Nav Tab">
118
- <NavItem.Tab active as={ CustomLink } href="https://random-website.com/">
118
+ <NavItem.Tab as={ CustomLink } href="#!">
119
119
  PHP
120
120
  </NavItem.Tab>
121
- <NavItem.Tab as={ CustomLink } href="https://wordpress.com">
121
+ <NavItem.Tab as={ CustomLink } href="#2!">
122
122
  WordPress
123
123
  </NavItem.Tab>
124
- <NavItem.Tab as={ CustomLink } href="https://newrelic.com/">
124
+ <NavItem.Tab active as={ CustomLink } href="#3!">
125
125
  New Relic
126
126
  </NavItem.Tab>
127
- <NavItem.Tab disabled as={ CustomLink } href="https://google.com/">
127
+ <NavItem.Tab as={ CustomLink } href="#4!">
128
+ Datadog
129
+ </NavItem.Tab>
130
+ <NavItem.Tab as={ CustomLink } href="#4!">
131
+ OnePlus
132
+ </NavItem.Tab>
133
+ <NavItem.Tab as={ CustomLink } href="#5!">
134
+ Rollbar
135
+ </NavItem.Tab>
136
+ <NavItem.Tab disabled as={ CustomLink } href="#6!">
128
137
  Not accessible
129
138
  </NavItem.Tab>
130
139
  </Nav.Tab>
@@ -163,7 +172,12 @@ export const Menu: Story = {
163
172
  Domains & TLS
164
173
  </NavItem.Menu>
165
174
 
166
- <NavItem.MenuGroup active label="Logs" renderIcon={ size => <BiHistory size={ size } /> }>
175
+ <NavItem.MenuGroup
176
+ active
177
+ activeChildren
178
+ label="Logs"
179
+ renderIcon={ size => <BiHistory size={ size } /> }
180
+ >
167
181
  <NavItem.Menu active as={ CustomLink } href="https://google.com/">
168
182
  Audit
169
183
  </NavItem.Menu>
@@ -24,7 +24,7 @@ const NavBase = forwardRef< HTMLElement, NavProps >(
24
24
  aria-label={ label }
25
25
  ref={ ref }
26
26
  className={ classNames( VIP_NAV, className ) }
27
- sx={ navStyles( variant ) }
27
+ sx={ navStyles( variant, orientation ) }
28
28
  orientation={ orientation }
29
29
  >
30
30
  <NavigationMenu.List
@@ -0,0 +1,50 @@
1
+ /** @jsxImportSource theme-ui */
2
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
3
+ // @ts-nocheck
4
+
5
+ import { render, screen } from '@testing-library/react';
6
+ import { axe } from 'jest-axe';
7
+ import { ThemeUIProvider } from 'theme-ui';
8
+
9
+ import { Nav } from './Nav';
10
+ import { NavItem } from './NavItem';
11
+ import { theme } from '../';
12
+ import { CustomLink } from '../utils/stories/CustomLink';
13
+
14
+ const renderWithTheme = children =>
15
+ render( <ThemeUIProvider theme={ theme }>{ children }</ThemeUIProvider> );
16
+
17
+ const renderComponent = () =>
18
+ renderWithTheme(
19
+ <Nav.Menu label="Nav Menu">
20
+ <NavItem.MenuGroup active activeChildren label="Logs">
21
+ <NavItem.Menu active as={ CustomLink } href="https://google.com/">
22
+ Audit
23
+ </NavItem.Menu>
24
+ <NavItem.Menu as={ CustomLink } href="https://wpvip.com/">
25
+ Runtime
26
+ </NavItem.Menu>
27
+ <NavItem.Menu as={ CustomLink } href="https://dashboard.wpvip.com/">
28
+ Slow Query
29
+ </NavItem.Menu>
30
+ </NavItem.MenuGroup>
31
+ </Nav.Menu>
32
+ );
33
+
34
+ describe( '<NavItemGroup />', () => {
35
+ it( 'renders the NavItemGroup component a data-active-children', async () => {
36
+ const { container } = renderComponent();
37
+
38
+ // Should find the button label
39
+ const button = screen.getByRole( 'button', { label: /Logs/ } );
40
+
41
+ expect( button ).toBeInTheDocument();
42
+
43
+ // Expect to have another attribute
44
+ expect( button ).toHaveAttribute( 'data-active', 'true' );
45
+ expect( button ).toHaveAttribute( 'data-active-children', 'true' );
46
+
47
+ // Check for accessibility issues
48
+ expect( await axe( container ) ).toHaveNoViolations();
49
+ } );
50
+ } );
@@ -17,6 +17,7 @@ import {
17
17
 
18
18
  export interface NavItemGroupProps extends NavItemBaseProps {
19
19
  renderIcon?: NavItemRenderIconProp;
20
+ activeChildren?: boolean;
20
21
  label: string;
21
22
  }
22
23
 
@@ -28,6 +29,7 @@ const NavItemGroupBase = forwardRef< HTMLLIElement, NavItemGroupProps >(
28
29
  orientation,
29
30
  className,
30
31
  active,
32
+ activeChildren,
31
33
  renderIcon,
32
34
  children,
33
35
  sx,
@@ -54,6 +56,7 @@ const NavItemGroupBase = forwardRef< HTMLLIElement, NavItemGroupProps >(
54
56
  aria-haspopup={ true }
55
57
  data-active={ active || undefined }
56
58
  data-open={ isExpanded || undefined }
59
+ data-active-children={ activeChildren || undefined }
57
60
  sx={ {
58
61
  ...navItemLinkVariantStyles( variant ),
59
62
  ...navItemGroupTriggerStyles,
@@ -17,6 +17,12 @@ export const menuItemStyles = ( orientation: NavProps[ 'orientation' ] ): ThemeU
17
17
  justifyContent: 'space-between',
18
18
  } );
19
19
 
20
+ // Tab Item Style <li>
21
+ export const tabItemStyles = ( orientation: NavProps[ 'orientation' ] ): ThemeUIStyleObject => ( {
22
+ ...defaultNavItemStyles( orientation ),
23
+ mr: 0,
24
+ } );
25
+
20
26
  // Menu Inverse Item Style <li>
21
27
  export const menuInverseItemStyles = (
22
28
  orientation: NavProps[ 'orientation' ]
@@ -31,9 +37,9 @@ export const menuInverseItemStyles = (
31
37
  // Menu Item Link <a>
32
38
 
33
39
  const visitedLink = '&:visited';
34
- const activeAfter = '&[data-active]::before';
35
- const active = '&[data-active]';
36
- const focusNotActiveHoverNotActive = '&:focus:not(&[data-active]), &:hover:not(&[data-active])';
40
+ const active = '&[data-active]:not(&[data-active-children="true"][data-state="open"])';
41
+ const activeBefore = `${ active }::before`;
42
+ const focusNotActiveHoverNotActive = `&:focus:not(${ active }), &:hover:not(${ active })`;
37
43
  const notHover = ':not(&:hover)';
38
44
  const svgIcon = 'svg';
39
45
 
@@ -63,7 +69,7 @@ export const menuItemLinkStyles: MixedStyleProp = {
63
69
  [ visitedLink ]: {
64
70
  color: 'text',
65
71
  },
66
- [ activeAfter ]: {
72
+ [ activeBefore ]: {
67
73
  position: 'absolute',
68
74
  content: "''",
69
75
  overflow: 'hidden',
@@ -1,27 +1,64 @@
1
1
  import { ThemeUIStyleObject } from 'theme-ui';
2
2
 
3
3
  import { defaultItemLinkStyles } from './primary';
4
+ import { NavProps } from '../../Nav';
4
5
 
5
6
  // Tab Root Styles <nav>
7
+ const getTabPropsByOrientation = ( orientation: NavProps[ 'orientation' ] ): ThemeUIStyleObject => {
8
+ if ( orientation === 'vertical' ) {
9
+ return {
10
+ '> div:first-of-type': {
11
+ height: '100%',
12
+ overflowY: 'auto',
13
+ },
14
+ ul: {
15
+ minHeight: 'max-content',
16
+ },
17
+ };
18
+ }
19
+ return {
20
+ '> div:first-of-type': {
21
+ width: '100%',
22
+ overflowX: 'auto',
23
+ },
24
+ ul: {
25
+ minWidth: 'max-content',
26
+ },
27
+ };
28
+ };
6
29
 
7
- export const tabRootStyles: ThemeUIStyleObject = {
30
+ export const tabRootStyles = ( orientation: NavProps[ 'orientation' ] ): ThemeUIStyleObject => ( {
8
31
  width: '100%',
9
32
  borderColor: 'borders.2',
10
- };
33
+ gap: 2,
34
+
35
+ // Responsive in case the content is bigger than the viewport
36
+ ...getTabPropsByOrientation( orientation ),
37
+ } );
11
38
 
12
39
  // Tab Link <a>
13
40
  export const tabItemLinkStyles: ThemeUIStyleObject = {
14
41
  ...defaultItemLinkStyles,
15
- px: 0,
16
- mr: 2,
42
+ px: 2,
43
+ height: '100%',
44
+ backgroundColor: 'red',
17
45
  color: 'heading',
18
46
  '&[data-active]': {
19
47
  color: 'link',
20
48
  fontWeight: 'normal',
21
- boxShadow: 'inset 0 -1px 0 0, 0 1px 0 0',
49
+ position: 'relative',
50
+ '&::after': {
51
+ position: 'absolute',
52
+ bottom: 0,
53
+ display: 'block',
54
+ width: '100%',
55
+ content: '""',
56
+ height: '0.125rem',
57
+ backgroundColor: 'link',
58
+ },
22
59
  },
23
60
  '&[aria-disabled="true"]': {
24
61
  color: 'muted',
25
62
  },
26
- ':hover': { fontWeight: 'regular', color: 'heading' },
63
+ ':hover': { fontWeight: 'regular', color: 'link' },
27
64
  };
@@ -8,6 +8,7 @@ import {
8
8
  menuInverseItemStyles,
9
9
  menuItemLinkStyles,
10
10
  menuItemStyles,
11
+ tabItemStyles,
11
12
  } from './styles/variants/menu';
12
13
  import {
13
14
  defaultNavItemStyles,
@@ -68,6 +69,9 @@ export const navItemStyles = (
68
69
  case 'menu': {
69
70
  return menuItemStyles( orientation );
70
71
  }
72
+ case 'tabs': {
73
+ return tabItemStyles( orientation );
74
+ }
71
75
  case 'menu-inverse': {
72
76
  return menuInverseItemStyles( orientation );
73
77
  }
@@ -82,29 +86,16 @@ export const navItemStyles = (
82
86
  }
83
87
  };
84
88
 
85
- export const navRootStyles = ( variant: NavVariant ): ThemeUIStyleObject => {
86
- switch ( variant ) {
87
- case 'tabs': {
88
- return tabRootStyles;
89
- }
90
-
91
- case 'toolbar': {
92
- return toolbarRootStyles;
93
- }
94
-
95
- default: {
96
- return defaultNavRootStyles;
97
- }
98
- }
99
- };
100
-
101
- export const navStyles = ( variant: NavVariant ): ThemeUIStyleObject => {
89
+ export const navStyles = (
90
+ variant: NavVariant,
91
+ orientation: NavProps[ 'orientation' ]
92
+ ): ThemeUIStyleObject => {
102
93
  let navStyle: ThemeUIStyleObject = {};
103
94
 
104
95
  switch ( variant ) {
105
96
  case 'tabs':
106
97
  {
107
- navStyle = tabRootStyles;
98
+ navStyle = tabRootStyles( orientation );
108
99
  }
109
100
 
110
101
  break;
@@ -38,6 +38,7 @@ export const Default = () => (
38
38
  </TabsContent>
39
39
  </Tabs>
40
40
  );
41
+
41
42
  export const SetActiveTab = () => {
42
43
  const [ activeTab, setActiveTab ] = React.useState( 'all' );
43
44
 
@@ -24,7 +24,16 @@ const styles = {
24
24
  '&[data-state="active"]': {
25
25
  color: 'link',
26
26
  fontWeight: 'regular',
27
- boxShadow: 'inset 0 -1px 0 0, 0 1px 0 0',
27
+ position: 'relative',
28
+ '&::after': {
29
+ position: 'absolute',
30
+ bottom: 0,
31
+ display: 'block',
32
+ width: '100%',
33
+ content: '""',
34
+ height: '0.125rem',
35
+ backgroundColor: 'link',
36
+ },
28
37
  },
29
38
  '&:disabled': {
30
39
  color: 'muted',
@@ -414,8 +414,10 @@ export default {
414
414
  '&:active': {
415
415
  color: 'links.active',
416
416
  },
417
+
417
418
  textDecorationThickness: '0.125rem',
418
- textUnderlineOffset: '0.125rem',
419
+ textUnderlineOffset: '0.250rem',
420
+
419
421
  '&:hover, &:focus': {
420
422
  color: 'links.hover',
421
423
  textDecorationLine: 'underline',
@@ -534,7 +536,8 @@ export default {
534
536
  a: {
535
537
  '&:hover': {
536
538
  textDecorationLine: 'underline',
537
- textDecorationThickness: '2px',
539
+ textDecorationThickness: '0.125rem',
540
+ textUnderlineOffset: '0.250rem',
538
541
  },
539
542
  },
540
543
  svg: {